From f3f82c1d187689ce200752c5580258407d35bc06 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sat, 8 Apr 2023 22:05:36 +0200 Subject: [PATCH 001/351] change /help-ping to add random member to thread --- .../help/commands/HelpPingSubcommand.java | 51 +++++++++++++------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java index 0e858364b..18ce1df79 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java @@ -1,12 +1,9 @@ package net.javadiscord.javabot.systems.help.commands; -import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -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.User; import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -14,18 +11,21 @@ import net.javadiscord.javabot.data.config.BotConfig; import net.javadiscord.javabot.data.config.GuildConfig; import net.javadiscord.javabot.data.config.guild.HelpConfig; +import net.javadiscord.javabot.util.ExceptionLogger; import net.javadiscord.javabot.util.Pair; import net.javadiscord.javabot.util.Responses; import org.jetbrains.annotations.NotNull; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; -import java.util.EnumSet; +import java.time.OffsetDateTime; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * Handler for the /help ping sub-command that allows users to occasionally ping @@ -74,25 +74,44 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { Responses.warning(event, "Sorry, but only the person who reserved this channel, or staff and helpers, may use this command.").queue(); return; } + if (post.getTimeCreated().isAfter(OffsetDateTime.now().minusSeconds(config.getHelpConfig().getHelpPingTimeoutSeconds()))) { + Responses.warning(event, "Sorry, this command cannot be used directly after a post has been created.").queue(); + return; + } if (isHelpPingTimeoutElapsed(member.getIdLong(), config)) { lastPingTimes.put(event.getMember().getIdLong(), new Pair<>(System.currentTimeMillis(), config.getGuild())); Role role = config.getHelpConfig().getHelpPingRole(); - event.getChannel().sendMessage(role.getAsMention()) - .setAllowedMentions(EnumSet.of(Message.MentionType.ROLE)) - .setEmbeds(buildAuthorEmbed(event.getUser())) - .queue(); - event.replyFormat("Successfully pinged " + role.getAsMention()).setEphemeral(true).queue(); + role.getGuild().findMembersWithRoles(role).onSuccess(members->{ + post.retrieveThreadMembers().queue(threadMembers -> { + Set memberIds = threadMembers + .stream() + .map(m->m.getIdLong()) + .collect(Collectors.toSet()); + members.removeIf(m -> + m.getOnlineStatus() == OnlineStatus.OFFLINE || + memberIds.contains(m.getIdLong())); + Collections.shuffle(members); + if(members.size()>0) { + post.addThreadMember(members.get(0)).queue(); + event.getHook().sendMessage("Successfully added a user with the help-ping role to the post.").setEphemeral(true).queue(); + }else { + event.getHook().sendMessage("Unfortunately, no available member with the help-ping role has been found.").queue(); + } + }, err -> { + event.getHook().sendMessage("An error occured trying to find available members").queue(); + ExceptionLogger.capture(err, HelpPingSubcommand.class.getName()); + }); + + }).onError(err -> { + event.getHook().sendMessage("An error occured trying to find available members").queue(); + ExceptionLogger.capture(err, HelpPingSubcommand.class.getName()); + }); + event.deferReply(true).queue(); } else { Responses.warning(event, "Sorry, but you can only use this command occasionally. Please try again later.").queue(); } } - private @NotNull MessageEmbed buildAuthorEmbed(@NotNull User author) { - return new EmbedBuilder() - .setTitle("Requested by " + author.getAsTag()) - .build(); - } - /** * Determines if a user is forbidden from sending a help-ping command due * to their status in the server. From 4dbd6be5c6efc42d4ecbcb160127b013d18ab483 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sat, 8 Apr 2023 22:09:52 +0200 Subject: [PATCH 002/351] make /help-ping non-ephemeral if successful --- .../javabot/systems/help/commands/HelpPingSubcommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java index 18ce1df79..c6ef60627 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java @@ -93,7 +93,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { Collections.shuffle(members); if(members.size()>0) { post.addThreadMember(members.get(0)).queue(); - event.getHook().sendMessage("Successfully added a user with the help-ping role to the post.").setEphemeral(true).queue(); + event.getHook().sendMessage("Successfully added a user with the help-ping role to the post.").setEphemeral(false).queue(); }else { event.getHook().sendMessage("Unfortunately, no available member with the help-ping role has been found.").queue(); } From 490da056f8ef58333292c36aaffa4758522223a5 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sat, 8 Apr 2023 22:12:51 +0200 Subject: [PATCH 003/351] fix ephemeral response in /help-ping --- .../javabot/systems/help/commands/HelpPingSubcommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java index c6ef60627..f015415cc 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java @@ -93,7 +93,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { Collections.shuffle(members); if(members.size()>0) { post.addThreadMember(members.get(0)).queue(); - event.getHook().sendMessage("Successfully added a user with the help-ping role to the post.").setEphemeral(false).queue(); + event.getHook().sendMessage("Successfully added a user with the help-ping role to the post.").queue(); }else { event.getHook().sendMessage("Unfortunately, no available member with the help-ping role has been found.").queue(); } @@ -106,7 +106,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { event.getHook().sendMessage("An error occured trying to find available members").queue(); ExceptionLogger.capture(err, HelpPingSubcommand.class.getName()); }); - event.deferReply(true).queue(); + event.deferReply(false).queue(); } else { Responses.warning(event, "Sorry, but you can only use this command occasionally. Please try again later.").queue(); } From 8dc1cfb78e5a444342c781b027b6dd60be0b356a Mon Sep 17 00:00:00 2001 From: Fischstaebchen Date: Sun, 30 Apr 2023 00:49:39 +0200 Subject: [PATCH 004/351] Changed the fallback value of the auto-indent option to match the enum constant defined in IndentationHelper.IndentationType. Simplified the IndentationHelper.isEscaped method. --- .../user_commands/format_code/FormatCodeCommand.java | 2 +- .../net/javadiscord/javabot/util/IndentationHelper.java | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/user_commands/format_code/FormatCodeCommand.java b/src/main/java/net/javadiscord/javabot/systems/user_commands/format_code/FormatCodeCommand.java index 4c1360a96..6d301eea3 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_commands/format_code/FormatCodeCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_commands/format_code/FormatCodeCommand.java @@ -65,7 +65,7 @@ public FormatCodeCommand() { public void execute(@NotNull SlashCommandInteractionEvent event) { OptionMapping idOption = event.getOption("message-id"); String format = event.getOption("format", "java", OptionMapping::getAsString); - String indentation = event.getOption("auto-indent","null",OptionMapping::getAsString); + String indentation = event.getOption("auto-indent","NULL",OptionMapping::getAsString); event.deferReply().queue(); if (idOption == null) { event.getChannel().getHistory() diff --git a/src/main/java/net/javadiscord/javabot/util/IndentationHelper.java b/src/main/java/net/javadiscord/javabot/util/IndentationHelper.java index a8c79bc55..93107c5a5 100644 --- a/src/main/java/net/javadiscord/javabot/util/IndentationHelper.java +++ b/src/main/java/net/javadiscord/javabot/util/IndentationHelper.java @@ -196,13 +196,8 @@ private IndentationState processTokenInCode(int i, char current) { */ private static boolean isEscaped(StringBuilder builder, int index) { int numberOfCharacters = 0; - index--; - if (index >= builder.length()) { - return false; - } - while (index > 0 && builder.charAt(index) == '\\') { + for (--index;index > 0 && index < builder.length() && builder.charAt(index) == '\\'; index--) { numberOfCharacters++; - index--; } return numberOfCharacters % 2 == 1; } From c60eddd69353025ca700555803de2367c045925f Mon Sep 17 00:00:00 2001 From: danthe1st Date: Mon, 1 May 2023 12:31:22 +0200 Subject: [PATCH 005/351] fix QOTW decrement embed --- .../notification/QOTWNotificationService.java | 16 ++++++++++++++++ .../qotw_points/ChangePointsSubcommand.java | 6 +++++- .../qotw_points/DecrementPointsSubcommand.java | 6 ++++++ .../qotw_points/IncrementPointsSubcommand.java | 6 ++++++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/notification/QOTWNotificationService.java b/src/main/java/net/javadiscord/javabot/systems/notification/QOTWNotificationService.java index dcd298a60..dfc880c82 100644 --- a/src/main/java/net/javadiscord/javabot/systems/notification/QOTWNotificationService.java +++ b/src/main/java/net/javadiscord/javabot/systems/notification/QOTWNotificationService.java @@ -49,6 +49,10 @@ public void sendAccountIncrementedNotification() { notificationService.withUser(user).sendDirectMessage(c -> c.sendMessageEmbeds(buildAccountIncrementEmbed(account.getPoints()))); } + public void sendAccountDecrementedNotification() { + notificationService.withUser(user).sendDirectMessage(c -> c.sendMessageEmbeds(buildAccountDecrementEmbed(account.getPoints()))); + } + /** * Sends a "your submission was declined"-notification to the {@link QOTWNotificationService#user}. * @@ -92,6 +96,17 @@ public void sendSubmissionDeclinedEmbed(SubmissionStatus status) { .build(); } + private @NotNull MessageEmbed buildAccountDecrementEmbed(long points) { + return buildQOTWNotificationEmbed() + .setColor(Responses.Type.WARN.getColor()) + .setDescription(String.format( + """ + Your QOTW score has been decremented. + **`1 QOTW-Point`** has been deducted from your score! (monthly total: %s) + In case you want to know why exactly this happened, please contact our staff team.""", points)) + .build(); + } + private @NotNull MessageEmbed buildSubmissionDeclinedEmbed(String reason) { return this.buildQOTWNotificationEmbed() .setColor(Responses.Type.ERROR.getColor()) @@ -102,4 +117,5 @@ public void sendSubmissionDeclinedEmbed(SubmissionStatus status) { user.getAsMention(), reason)) .build(); } + } diff --git a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/ChangePointsSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/ChangePointsSubcommand.java index c0f1c2913..84596652a 100644 --- a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/ChangePointsSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/ChangePointsSubcommand.java @@ -10,6 +10,7 @@ import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; import net.javadiscord.javabot.systems.notification.NotificationService; +import net.javadiscord.javabot.systems.notification.QOTWNotificationService; import net.javadiscord.javabot.systems.qotw.QOTWPointsService; import net.javadiscord.javabot.util.Responses; import org.jetbrains.annotations.NotNull; @@ -68,11 +69,14 @@ private void sendNotifications(SlashCommandInteractionEvent event, Member member MessageEmbed embed = buildIncrementEmbed(member.getUser(), points); notificationService.withGuild(event.getGuild()).sendToModerationLog(c -> c.sendMessageEmbeds(embed)); if (!quiet) { - notificationService.withQOTW(event.getGuild(), member.getUser()).sendAccountIncrementedNotification(); + sendUserNotification(notificationService.withQOTW(event.getGuild(), member.getUser())); + } event.getHook().sendMessageEmbeds(embed).queue(); } + protected abstract void sendUserNotification(@NotNull QOTWNotificationService notificationService); + protected @NotNull MessageEmbed buildIncrementEmbed(@NotNull User user, long points) { return createIncrementEmbedBuilder(user, points) .build(); diff --git a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/DecrementPointsSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/DecrementPointsSubcommand.java index 1cb40cc63..f2229480f 100644 --- a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/DecrementPointsSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/DecrementPointsSubcommand.java @@ -7,6 +7,7 @@ import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.javadiscord.javabot.systems.notification.NotificationService; +import net.javadiscord.javabot.systems.notification.QOTWNotificationService; import net.javadiscord.javabot.systems.qotw.QOTWPointsService; /** @@ -29,4 +30,9 @@ protected int getIncrementCount(Member targetMember, SlashCommandInteractionEven return super.createIncrementEmbedBuilder(user, points) .setTitle("QOTW Account decremented"); } + + @Override + protected void sendUserNotification(@NotNull QOTWNotificationService notificationService) { + notificationService.sendAccountDecrementedNotification(); + } } diff --git a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/IncrementPointsSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/IncrementPointsSubcommand.java index 491741eb4..0f49b0e4d 100644 --- a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/IncrementPointsSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/IncrementPointsSubcommand.java @@ -7,6 +7,7 @@ import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.javadiscord.javabot.systems.notification.NotificationService; +import net.javadiscord.javabot.systems.notification.QOTWNotificationService; import net.javadiscord.javabot.systems.qotw.QOTWPointsService; /** @@ -30,4 +31,9 @@ protected int getIncrementCount(Member targetMember, SlashCommandInteractionEven .setTitle("QOTW Account incremented"); } + @Override + protected void sendUserNotification(@NotNull QOTWNotificationService notificationService) { + notificationService.sendAccountIncrementedNotification(); + } + } From f86b939838d8939f08c03a0fcaa9df95253592df Mon Sep 17 00:00:00 2001 From: 0x3C50 <99053360+0x3C50@users.noreply.github.com> Date: Sun, 7 May 2023 15:30:49 +0200 Subject: [PATCH 006/351] Improve HugListener to accept "fucker", "fuckkkk...", and to use regex instead --- .../javabot/listener/HugListener.java | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/listener/HugListener.java b/src/main/java/net/javadiscord/javabot/listener/HugListener.java index c455da7f9..d078542b8 100644 --- a/src/main/java/net/javadiscord/javabot/listener/HugListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/HugListener.java @@ -14,6 +14,8 @@ import net.javadiscord.javabot.util.WebhookUtil; import javax.annotation.Nonnull; +import java.util.Objects; +import java.util.regex.Pattern; /** * Replaces all occurrences of 'fuck' in incoming messages with 'hug'. @@ -24,6 +26,20 @@ public class HugListener extends ListenerAdapter { private final AutoMod autoMod; private final BotConfig botConfig; + private static final Pattern FUCKER = Pattern.compile("(fuck)(ing|er|k+)?", Pattern.CASE_INSENSITIVE); + + private static String processFuck(String str) { + return FUCKER.matcher(str).replaceAll(matchResult -> { + String theFuck = matchResult.group(1); + String suffix = Objects.requireNonNullElse(matchResult.group(2), ""); + String processedSuffix = switch(suffix.toLowerCase()) { + case "er", "ing" -> copyCase(suffix, 0, 'g') + suffix; + default -> suffix.startsWith("k") ? "g".repeat(suffix.length()) : ""; + }; + return processHug(theFuck) + processedSuffix; + }); + } + @Override public void onMessageReceived(@Nonnull MessageReceivedEvent event) { if (!event.isFromGuild()) { @@ -57,38 +73,22 @@ public void onMessageReceived(@Nonnull MessageReceivedEvent event) { } final TextChannel textChannel = tc; String content = event.getMessage().getContentRaw(); - String lowerCaseContent = content.toLowerCase(); - if (lowerCaseContent.contains("fuck")) { + if (FUCKER.matcher(content).find()) { long threadId = event.isFromThread() ? event.getChannel().getIdLong() : 0; - StringBuilder sb = new StringBuilder(content.length()); - int index = 0; - int indexBkp = index; - while ((index = lowerCaseContent.indexOf("fuck", index)) != -1) { - sb.append(content.substring(indexBkp, index)); - sb.append(loadHug(content, index)); - indexBkp = index++ + 4; - if (content.length() >= indexBkp + 3 && "ing".equals(lowerCaseContent.substring(indexBkp, indexBkp + 3))) { - sb.append(copyCase(content, indexBkp-1, 'g')); - sb.append(content.substring(indexBkp, indexBkp + 3)); - index+=3; - indexBkp+=3; - } - } - - sb.append(content.substring(indexBkp)); WebhookUtil.ensureWebhookExists(textChannel, - wh -> sendWebhookMessage(wh, event.getMessage(), sb.toString(), threadId), + wh -> sendWebhookMessage(wh, event.getMessage(), processFuck(content), threadId), e -> ExceptionLogger.capture(e, getClass().getSimpleName())); } } - private String loadHug(String originalText, int startIndex) { - return copyCase(originalText, startIndex, 'h') + "" - + copyCase(originalText, startIndex + 1, 'u') + "" - + copyCase(originalText, startIndex + 3, 'g'); + private static String processHug(String originalText) { + // FucK -> HuG, FuCk -> Hug + return String.valueOf(copyCase(originalText, 0, 'h')) + + copyCase(originalText, 1, 'u') + + copyCase(originalText, 3, 'g'); } - private char copyCase(String original, int index, char newChar) { + private static char copyCase(String original, int index, char newChar) { if (Character.isUpperCase(original.charAt(index))) { return Character.toUpperCase(newChar); } else { From a60adbee859f10d37fc03000c5083640e60cc7d2 Mon Sep 17 00:00:00 2001 From: 0x3C50 <99053360+0x3C50@users.noreply.github.com> Date: Sun, 7 May 2023 15:37:28 +0200 Subject: [PATCH 007/351] fix upper- / lowercase detection --- .../javabot/listener/HugListener.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/listener/HugListener.java b/src/main/java/net/javadiscord/javabot/listener/HugListener.java index d078542b8..b14e79e9f 100644 --- a/src/main/java/net/javadiscord/javabot/listener/HugListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/HugListener.java @@ -2,7 +2,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.Webhook; import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.middleman.StandardGuildChannel; @@ -34,12 +35,25 @@ private static String processFuck(String str) { String suffix = Objects.requireNonNullElse(matchResult.group(2), ""); String processedSuffix = switch(suffix.toLowerCase()) { case "er", "ing" -> copyCase(suffix, 0, 'g') + suffix; - default -> suffix.startsWith("k") ? "g".repeat(suffix.length()) : ""; + default -> suffix.toLowerCase().startsWith("k") ? copyCase(suffix, "g".repeat(suffix.length())) : ""; }; return processHug(theFuck) + processedSuffix; }); } + private static String copyCase(String source, String toChange) { + if (source.length() != toChange.length()) throw new IllegalArgumentException("lengths differ"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < source.length(); i++) { + char a = source.charAt(i); + char b = toChange.charAt(i); + if (Character.isUpperCase(a)) b = Character.toUpperCase(b); + else b = Character.toLowerCase(b); + sb.append(b); + } + return sb.toString(); + } + @Override public void onMessageReceived(@Nonnull MessageReceivedEvent event) { if (!event.isFromGuild()) { From 4b11c37db103172d3117a563868bb2c3a654b200 Mon Sep 17 00:00:00 2001 From: 0x3C50 <99053360+0x3C50@users.noreply.github.com> Date: Sun, 7 May 2023 15:54:29 +0200 Subject: [PATCH 008/351] code cleanup --- .../javabot/listener/HugListener.java | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/listener/HugListener.java b/src/main/java/net/javadiscord/javabot/listener/HugListener.java index b14e79e9f..436382248 100644 --- a/src/main/java/net/javadiscord/javabot/listener/HugListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/HugListener.java @@ -29,31 +29,6 @@ public class HugListener extends ListenerAdapter { private static final Pattern FUCKER = Pattern.compile("(fuck)(ing|er|k+)?", Pattern.CASE_INSENSITIVE); - private static String processFuck(String str) { - return FUCKER.matcher(str).replaceAll(matchResult -> { - String theFuck = matchResult.group(1); - String suffix = Objects.requireNonNullElse(matchResult.group(2), ""); - String processedSuffix = switch(suffix.toLowerCase()) { - case "er", "ing" -> copyCase(suffix, 0, 'g') + suffix; - default -> suffix.toLowerCase().startsWith("k") ? copyCase(suffix, "g".repeat(suffix.length())) : ""; - }; - return processHug(theFuck) + processedSuffix; - }); - } - - private static String copyCase(String source, String toChange) { - if (source.length() != toChange.length()) throw new IllegalArgumentException("lengths differ"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < source.length(); i++) { - char a = source.charAt(i); - char b = toChange.charAt(i); - if (Character.isUpperCase(a)) b = Character.toUpperCase(b); - else b = Character.toLowerCase(b); - sb.append(b); - } - return sb.toString(); - } - @Override public void onMessageReceived(@Nonnull MessageReceivedEvent event) { if (!event.isFromGuild()) { @@ -85,12 +60,11 @@ public void onMessageReceived(@Nonnull MessageReceivedEvent event) { if (tc == null) { return; } - final TextChannel textChannel = tc; String content = event.getMessage().getContentRaw(); if (FUCKER.matcher(content).find()) { long threadId = event.isFromThread() ? event.getChannel().getIdLong() : 0; - WebhookUtil.ensureWebhookExists(textChannel, - wh -> sendWebhookMessage(wh, event.getMessage(), processFuck(content), threadId), + WebhookUtil.ensureWebhookExists(tc, + wh -> sendWebhookMessage(wh, event.getMessage(), replaceFucks(content), threadId), e -> ExceptionLogger.capture(e, getClass().getSimpleName())); } } @@ -102,6 +76,32 @@ private static String processHug(String originalText) { + copyCase(originalText, 3, 'g'); } + private static String replaceFucks(String str) { + return FUCKER.matcher(str).replaceAll(matchResult -> { + String theFuck = matchResult.group(1); + String suffix = Objects.requireNonNullElse(matchResult.group(2), ""); + String processedSuffix = switch(suffix.toLowerCase()) { + case "er", "ing" -> copyCase(suffix, 0, 'g') + suffix; // fucking, fucker + case "" -> ""; // just fuck + default -> copyCase(suffix, "g".repeat(suffix.length())); // fuckkkkk... + }; + return processHug(theFuck) + processedSuffix; + }); + } + + private static String copyCase(String source, String toChange) { + if (source.length() != toChange.length()) throw new IllegalArgumentException("lengths differ"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < source.length(); i++) { + char a = source.charAt(i); + char b = toChange.charAt(i); + if (Character.isUpperCase(a)) b = Character.toUpperCase(b); + else b = Character.toLowerCase(b); + sb.append(b); + } + return sb.toString(); + } + private static char copyCase(String original, int index, char newChar) { if (Character.isUpperCase(original.charAt(index))) { return Character.toUpperCase(newChar); From 2d9267056d07897fc4edd44799316e8f1e6b689d Mon Sep 17 00:00:00 2001 From: 0x3C50 <99053360+0x3C50@users.noreply.github.com> Date: Sun, 7 May 2023 15:59:32 +0200 Subject: [PATCH 009/351] checkstyle --- .../java/net/javadiscord/javabot/listener/HugListener.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/listener/HugListener.java b/src/main/java/net/javadiscord/javabot/listener/HugListener.java index 436382248..fd0894e29 100644 --- a/src/main/java/net/javadiscord/javabot/listener/HugListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/HugListener.java @@ -24,11 +24,10 @@ @Slf4j @RequiredArgsConstructor public class HugListener extends ListenerAdapter { + private static final Pattern FUCKER = Pattern.compile("(fuck)(ing|er|k+)?", Pattern.CASE_INSENSITIVE); private final AutoMod autoMod; private final BotConfig botConfig; - private static final Pattern FUCKER = Pattern.compile("(fuck)(ing|er|k+)?", Pattern.CASE_INSENSITIVE); - @Override public void onMessageReceived(@Nonnull MessageReceivedEvent event) { if (!event.isFromGuild()) { From 3d7f6ef15ee1e3e5e9d276aa931ff9acbf4c4f12 Mon Sep 17 00:00:00 2001 From: 0x3C50 <99053360+0x3C50@users.noreply.github.com> Date: Sun, 7 May 2023 16:17:05 +0200 Subject: [PATCH 010/351] more code cleanup --- .../java/net/javadiscord/javabot/listener/HugListener.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/listener/HugListener.java b/src/main/java/net/javadiscord/javabot/listener/HugListener.java index fd0894e29..7e78c66a7 100644 --- a/src/main/java/net/javadiscord/javabot/listener/HugListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/HugListener.java @@ -92,11 +92,7 @@ private static String copyCase(String source, String toChange) { if (source.length() != toChange.length()) throw new IllegalArgumentException("lengths differ"); StringBuilder sb = new StringBuilder(); for (int i = 0; i < source.length(); i++) { - char a = source.charAt(i); - char b = toChange.charAt(i); - if (Character.isUpperCase(a)) b = Character.toUpperCase(b); - else b = Character.toLowerCase(b); - sb.append(b); + sb.append(copyCase(source, i, toChange.charAt(i))); } return sb.toString(); } From 45ea5c5289c523e8ed303dfdb5ca7b0fdfdd79e4 Mon Sep 17 00:00:00 2001 From: 0x3C50 <99053360+0x3C50@users.noreply.github.com> Date: Sun, 7 May 2023 18:50:22 +0200 Subject: [PATCH 011/351] add "fucked" -> "hugged" --- .../java/net/javadiscord/javabot/listener/HugListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/listener/HugListener.java b/src/main/java/net/javadiscord/javabot/listener/HugListener.java index 7e78c66a7..18ed84d85 100644 --- a/src/main/java/net/javadiscord/javabot/listener/HugListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/HugListener.java @@ -24,7 +24,7 @@ @Slf4j @RequiredArgsConstructor public class HugListener extends ListenerAdapter { - private static final Pattern FUCKER = Pattern.compile("(fuck)(ing|er|k+)?", Pattern.CASE_INSENSITIVE); + private static final Pattern FUCKER = Pattern.compile("(fuck)(ing|er|ed|k+)?", Pattern.CASE_INSENSITIVE); private final AutoMod autoMod; private final BotConfig botConfig; @@ -80,7 +80,7 @@ private static String replaceFucks(String str) { String theFuck = matchResult.group(1); String suffix = Objects.requireNonNullElse(matchResult.group(2), ""); String processedSuffix = switch(suffix.toLowerCase()) { - case "er", "ing" -> copyCase(suffix, 0, 'g') + suffix; // fucking, fucker + case "er", "ed", "ing" -> copyCase(suffix, 0, 'g') + suffix; // fucking, fucker case "" -> ""; // just fuck default -> copyCase(suffix, "g".repeat(suffix.length())); // fuckkkkk... }; From f07f34346b5757a315797438b2a547631f394d35 Mon Sep 17 00:00:00 2001 From: 0x3C50 <99053360+0x3C50@users.noreply.github.com> Date: Sun, 7 May 2023 18:58:50 +0200 Subject: [PATCH 012/351] add fucked comment --- .../javadiscord/javabot/listener/HugListener.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/javadiscord/javabot/listener/HugListener.java b/src/main/java/net/javadiscord/javabot/listener/HugListener.java index 18ed84d85..27c012a03 100644 --- a/src/main/java/net/javadiscord/javabot/listener/HugListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/HugListener.java @@ -28,6 +28,17 @@ public class HugListener extends ListenerAdapter { private final AutoMod autoMod; private final BotConfig botConfig; + public static void main(String[] args) { + String str = """ + this is FUCKED, Absolutely FuckEd + you FuckEr + FuckKkKkK + fuck + """.trim(); + System.out.println(str); + System.out.println(replaceFucks(str)); + } + @Override public void onMessageReceived(@Nonnull MessageReceivedEvent event) { if (!event.isFromGuild()) { @@ -80,7 +91,7 @@ private static String replaceFucks(String str) { String theFuck = matchResult.group(1); String suffix = Objects.requireNonNullElse(matchResult.group(2), ""); String processedSuffix = switch(suffix.toLowerCase()) { - case "er", "ed", "ing" -> copyCase(suffix, 0, 'g') + suffix; // fucking, fucker + case "er", "ed", "ing" -> copyCase(suffix, 0, 'g') + suffix; // fucking, fucker, fucked case "" -> ""; // just fuck default -> copyCase(suffix, "g".repeat(suffix.length())); // fuckkkkk... }; From dcf77a08fec249a20dabb25bf829824acbe1cf8c Mon Sep 17 00:00:00 2001 From: 0x3C50 <99053360+0x3C50@users.noreply.github.com> Date: Sun, 7 May 2023 19:02:07 +0200 Subject: [PATCH 013/351] oops, forgot main method --- .../net/javadiscord/javabot/listener/HugListener.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/listener/HugListener.java b/src/main/java/net/javadiscord/javabot/listener/HugListener.java index 27c012a03..165df00ed 100644 --- a/src/main/java/net/javadiscord/javabot/listener/HugListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/HugListener.java @@ -28,17 +28,6 @@ public class HugListener extends ListenerAdapter { private final AutoMod autoMod; private final BotConfig botConfig; - public static void main(String[] args) { - String str = """ - this is FUCKED, Absolutely FuckEd - you FuckEr - FuckKkKkK - fuck - """.trim(); - System.out.println(str); - System.out.println(replaceFucks(str)); - } - @Override public void onMessageReceived(@Nonnull MessageReceivedEvent event) { if (!event.isFromGuild()) { From e0bd2bdd6e6d9a35cfa76fe1e3b614fe5032039d Mon Sep 17 00:00:00 2001 From: danthe1st Date: Tue, 13 Jun 2023 17:11:51 +0200 Subject: [PATCH 014/351] warn button and reason selector in report menu --- .../moderation/report/ReportManager.java | 3 +- .../javabot/util/InteractionUtils.java | 136 +++++++++++++++--- 2 files changed, 118 insertions(+), 21 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java b/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java index 81d162fbb..7f226fe77 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java @@ -175,7 +175,8 @@ private ActionRow setComponents(long targetId, long threadId) { return ActionRow.of( Button.secondary(ComponentIdBuilder.build("resolve-report", threadId), "Mark as resolved"), Button.danger(String.format(InteractionUtils.BAN_TEMPLATE, targetId), "Ban"), - Button.danger(String.format(InteractionUtils.KICK_TEMPLATE, targetId), "Kick") + Button.danger(String.format(InteractionUtils.KICK_TEMPLATE, targetId), "Kick"), + Button.primary(String.format(InteractionUtils.WARN_TEMPLATE, targetId), "Warn") ); } diff --git a/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java b/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java index e77b6f700..d0d2886ff 100644 --- a/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java +++ b/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java @@ -2,24 +2,44 @@ import xyz.dynxsty.dih4jda.util.ComponentIdBuilder; import xyz.dynxsty.dih4jda.interactions.components.ButtonHandler; - +import xyz.dynxsty.dih4jda.interactions.components.ModalHandler; +import xyz.dynxsty.dih4jda.interactions.components.StringSelectMenuHandler; import lombok.RequiredArgsConstructor; +import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; +import net.dv8tion.jda.api.interactions.components.ActionRow; import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.interactions.components.buttons.ButtonInteraction; +import net.dv8tion.jda.api.interactions.components.selections.SelectOption; +import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu; +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.interactions.modals.ModalInteraction; +import net.dv8tion.jda.api.interactions.modals.ModalMapping; +import net.dv8tion.jda.api.requests.restaction.interactions.InteractionCallbackAction; +import net.dv8tion.jda.api.requests.restaction.interactions.ModalCallbackAction; import net.javadiscord.javabot.annotations.AutoDetectableComponentHandler; import net.javadiscord.javabot.data.config.BotConfig; import net.javadiscord.javabot.data.config.GuildConfig; import net.javadiscord.javabot.data.h2db.DbHelper; import net.javadiscord.javabot.systems.moderation.ModerationService; import net.javadiscord.javabot.systems.moderation.warn.dao.WarnRepository; +import net.javadiscord.javabot.systems.moderation.warn.model.WarnSeverity; import net.javadiscord.javabot.systems.notification.NotificationService; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.ExecutorService; +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; + import org.jetbrains.annotations.NotNull; /** @@ -27,7 +47,7 @@ */ @AutoDetectableComponentHandler("utils") @RequiredArgsConstructor -public class InteractionUtils implements ButtonHandler { +public class InteractionUtils implements ButtonHandler, ModalHandler, StringSelectMenuHandler { /** * Template Interaction ID for deleting the original Message. */ @@ -60,12 +80,13 @@ public class InteractionUtils implements ButtonHandler { * of the message, a staff member, or the owner. * * @param interaction The button interaction. + * @return the {@link ReplyCallbackAction} for responding to the request */ - private void delete(@NotNull ButtonInteraction interaction) { + @CheckReturnValue + private InteractionCallbackAction delete(@NotNull ButtonInteraction interaction) { Member member = interaction.getMember(); if (member == null) { - Responses.warning(interaction.getHook(), "Could not get member.").queue(); - return; + return Responses.warning(interaction, "Could not get member."); } GuildConfig config = botConfig.get(interaction.getGuild()); Message msg = interaction.getMessage(); @@ -75,50 +96,125 @@ private void delete(@NotNull ButtonInteraction interaction) { member.isOwner() ) { msg.delete().queue(); + return interaction.deferEdit(); } else { - Responses.warning(interaction.getHook(), "You don't have permission to delete this message.").queue(); + return Responses.warning(interaction, "You don't have permission to delete this message."); } } - private void kick(ButtonInteraction interaction, @NotNull Guild guild, String memberId) { + private void kick(ModalInteraction interaction, @NotNull Guild guild, String memberId, String reason) { + if(!interaction.getMember().hasPermission(Permission.KICK_MEMBERS)) { + Responses.error(interaction.getHook(), "Missing permissions").queue(); + return; + } + ModerationService service = new ModerationService(notificationService, botConfig, interaction, warnRepository, asyncPool); + guild.retrieveMemberById(memberId).queue( + member -> { + service.kick(member.getUser(), reason, interaction.getMember(), interaction.getMessageChannel(), false); + interaction.getMessage().editMessageComponents(ActionRow.of(Button.danger(interaction.getModalId(), "Kicked by "+interaction.getUser().getAsTag()).asDisabled())).queue(); + }, error -> Responses.error(interaction.getHook(), "Could not find member: " + error.getMessage()).queue() + ); + } + + private void warn(ModalInteraction interaction, @NotNull Guild guild, String memberId, WarnSeverity severity, String reason) { + if(!interaction.getMember().hasPermission(Permission.MODERATE_MEMBERS)) { + Responses.error(interaction.getHook(), "Missing permissions").queue(); + return; + } ModerationService service = new ModerationService(notificationService, botConfig, interaction, warnRepository, asyncPool); guild.retrieveMemberById(memberId).queue( member -> { - service.kick(member.getUser(), "None", interaction.getMember(), interaction.getMessageChannel(), false); - interaction.editButton(interaction.getButton().withLabel("Kicked by " + interaction.getUser().getAsTag()).asDisabled()).queue(); + service.warn(member.getUser(), severity, reason, interaction.getMember(), interaction.getMessageChannel(), false); + interaction.getHook().editOriginalComponents(ActionRow.of(Button.primary(interaction.getModalId(), "Warned by "+interaction.getUser().getAsTag()).asDisabled())).queue(); }, error -> Responses.error(interaction.getHook(), "Could not find member: " + error.getMessage()).queue() ); } - private void ban(ButtonInteraction interaction, @NotNull Guild guild, String memberId) { + private void ban(ModalInteraction interaction, @NotNull Guild guild, String memberId, String reason) { + if(!interaction.getMember().hasPermission(Permission.BAN_MEMBERS)) { + Responses.error(interaction.getHook(), "Missing permissions").queue(); + return; + } ModerationService service = new ModerationService(notificationService, botConfig, interaction, warnRepository, asyncPool); guild.getJDA().retrieveUserById(memberId).queue( user -> { - service.ban(user, "None", interaction.getMember(), interaction.getMessageChannel(), false); - interaction.editButton(interaction.getButton().withLabel("Banned by " + interaction.getUser().getAsTag()).asDisabled()).queue(); + service.ban(user, reason, interaction.getMember(), interaction.getMessageChannel(), false); + interaction.getMessage().editMessageComponents(ActionRow.of(Button.danger(interaction.getModalId(), "Banned by "+interaction.getUser().getAsTag()).asDisabled())).queue(); }, error -> Responses.error(interaction.getHook(), "Could not find member: " + error.getMessage()).queue() ); } - private void unban(ButtonInteraction interaction, long memberId) { + private void unban(ModalInteraction interaction, long memberId, String reason) { + if(!interaction.getMember().hasPermission(Permission.BAN_MEMBERS)) { + Responses.error(interaction.getHook(), "Missing permissions").queue(); + return; + } ModerationService service = new ModerationService(notificationService, botConfig, interaction, warnRepository, asyncPool); - service.unban(memberId, "None", interaction.getMember(), interaction.getMessageChannel(), false); - interaction.editButton(interaction.getButton().withLabel("Unbanned by " + interaction.getUser().getAsTag()).asDisabled()).queue(); + service.unban(memberId, reason, interaction.getMember(), interaction.getMessageChannel(), false); + interaction.getMessage().editMessageComponents(ActionRow.of(Button.secondary(interaction.getModalId(), "Unbanned by "+interaction.getUser().getAsTag()).asDisabled())).queue(); } @Override public void handleButton(@NotNull ButtonInteractionEvent event, @NotNull Button button) { - event.deferEdit().queue(); String[] id = ComponentIdBuilder.split(event.getComponentId()); if (event.getGuild() == null) { Responses.error(event.getHook(), "This button may only be used in context of a server.").queue(); return; } + + (switch (id[1]) { + case "delete" -> delete(event.getInteraction()); + case "kick" -> generateModal(event, "Kick user"); + case "ban" -> generateModal(event, "Ban user"); + case "unban" -> generateModal(event, "Unban user"); + case "warn" -> event.replyComponents(ActionRow.of(StringSelectMenu.create(event.getComponentId()) + .addOptions(Arrays.stream(WarnSeverity.values()) + .map(severity -> SelectOption.of(severity.toString(), severity.name())) + .toArray(SelectOption[]::new)) + .build())).setEphemeral(true); + default -> event.reply("Invalid action"); + }).queue(); + } + + private ModalCallbackAction generateModal(ButtonInteractionEvent event, String title) { + return event.replyModal(Modal.create(event.getComponentId(), title) + .addActionRow(TextInput.create("reason", "Reason", TextInputStyle.SHORT).setRequired(true).build()) + .build()); + } + + @Override + public void handleModal(@Nonnull ModalInteractionEvent event, @Nonnull List mappings) { + event.deferEdit().queue(); + String[] id = ComponentIdBuilder.split(event.getModalId()); + if (event.getGuild() == null) { + Responses.error(event.getHook(), "This button may only be used in context of a server.").queue(); + return; + } + String reason = "None"; + WarnSeverity severity=WarnSeverity.MEDIUM; + + for (ModalMapping mapping : mappings) { + if ("reason".equals(mapping.getId())) { + reason = mapping.getAsString(); + } + if ("severity".equals(mapping.getId())) { + severity=WarnSeverity.valueOf(mapping.getAsString()); + } + } + switch (id[1]) { - case "delete" -> delete(event.getInteraction()); - case "kick" -> kick(event.getInteraction(), event.getGuild(), id[2]); - case "ban" -> ban(event.getInteraction(), event.getGuild(), id[2]); - case "unban" -> unban(event.getInteraction(), Long.parseLong(id[2])); + case "kick" -> kick(event.getInteraction(), event.getGuild(), id[2], reason); + case "ban" -> ban(event.getInteraction(), event.getGuild(), id[2], reason); + case "warn" -> warn(event.getInteraction(), event.getGuild(), id[2], severity, reason); + case "unban" -> unban(event.getInteraction(), Long.parseLong(id[2]), reason); } } + + @Override + public void handleStringSelectMenu(@Nonnull StringSelectInteractionEvent event, @Nonnull List options) { + event.replyModal(Modal.create(event.getComponentId()+":"+options.get(0), "Warn user") + .addActionRow(TextInput.create("reason", "Reason", TextInputStyle.SHORT).setRequired(true).build()) + .build()) + .queue(); + } } From 03fa9d5200de6b59594a05bb0219c8e3d7de9301 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sun, 18 Jun 2023 20:46:11 +0200 Subject: [PATCH 015/351] fix severity with warn button --- .../java/net/javadiscord/javabot/util/InteractionUtils.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java b/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java index d0d2886ff..7b22c4682 100644 --- a/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java +++ b/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java @@ -191,15 +191,12 @@ public void handleModal(@Nonnull ModalInteractionEvent event, @Nonnull List 3 ? WarnSeverity.valueOf(id[3]) : null; for (ModalMapping mapping : mappings) { if ("reason".equals(mapping.getId())) { reason = mapping.getAsString(); } - if ("severity".equals(mapping.getId())) { - severity=WarnSeverity.valueOf(mapping.getAsString()); - } } switch (id[1]) { From 693366ed6dbe1e414eaa28c654af77c392f79465 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sun, 18 Jun 2023 20:48:55 +0200 Subject: [PATCH 016/351] make warn severity check more robust --- .../net/javadiscord/javabot/util/InteractionUtils.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java b/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java index 7b22c4682..aa224a9a9 100644 --- a/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java +++ b/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java @@ -191,7 +191,15 @@ public void handleModal(@Nonnull ModalInteractionEvent event, @Nonnull List 3 ? WarnSeverity.valueOf(id[3]) : null; + WarnSeverity severity = WarnSeverity.MEDIUM; + + if (id.length > 3) { + try { + severity = WarnSeverity.valueOf(id[3]); + } catch (IllegalArgumentException e) { + ExceptionLogger.capture(e, "Cannot load warn severity"); + } + } for (ModalMapping mapping : mappings) { if ("reason".equals(mapping.getId())) { From 5a1d0918a9f166157bed8b853646c0db6e68c5c5 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sat, 8 Jul 2023 13:04:53 +0200 Subject: [PATCH 017/351] XP and DM notification for dormant channels --- .../javabot/data/config/guild/HelpConfig.java | 12 +++++++ .../systems/help/HelpExperienceService.java | 27 +++++++++++++- .../systems/help/HelpForumUpdater.java | 36 +++++++++++++++++++ .../javabot/systems/help/HelpListener.java | 19 +++------- .../javabot/systems/help/HelpManager.java | 16 ++++----- .../help/dao/HelpTransactionRepository.java | 19 ++++++++-- .../systems/help/model/HelpTransaction.java | 1 + .../user_preferences/model/Preference.java | 6 +++- .../07-08-2023_channel_transactions.sql | 1 + 9 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 src/main/resources/database/migrations/07-08-2023_channel_transactions.sql diff --git a/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java b/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java index 760d27524..893028952 100644 --- a/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java +++ b/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java @@ -38,6 +38,18 @@ public class HelpConfig extends GuildConfigItem { */ private String dormantChannelMessageTemplate = "`\uD83D\uDCA4` **Post marked as dormant**\n> This post has been inactive for over %s minutes, thus, it has been **archived**.\n> If your question was not answered yet, feel free to re-open this post or create a new one."; + /** + * The message that's sent in a post to tell users that it + * is now marked as dormant and no more messages can be sent. + */ + private String dormantChannelPrivateMessageTemplate = """ + Your post %s in %s has been inactive for over %s minutes, thus, it has been **archived**. + If your question was not answered yet, feel free to re-open this post by sending another message in that channel. + You can disable notifications like this using the `/preferences` command. + [Post link](%s) + """; + + /** * The message that's sent when a user unreserved a channel where other users * participated in. diff --git a/src/main/java/net/javadiscord/javabot/systems/help/HelpExperienceService.java b/src/main/java/net/javadiscord/javabot/systems/help/HelpExperienceService.java index 011f67395..03997770c 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/HelpExperienceService.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/HelpExperienceService.java @@ -4,7 +4,9 @@ import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.javadiscord.javabot.data.config.BotConfig; +import net.javadiscord.javabot.data.config.guild.HelpConfig; import net.javadiscord.javabot.systems.help.dao.HelpAccountRepository; import net.javadiscord.javabot.systems.help.dao.HelpTransactionRepository; import net.javadiscord.javabot.systems.help.model.HelpAccount; @@ -18,6 +20,7 @@ import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; /** @@ -75,10 +78,11 @@ public List getTopAccounts(int amount, int page) { * @param recipient The recipient's user id. * @param value The transaction's value. * @param guild The current guild. + * @param channelId The ID of the channel the transaction should be performed in * @throws DataAccessException If an error occurs. */ @Transactional - public void performTransaction(long recipient, double value, Guild guild) throws DataAccessException { + public void performTransaction(long recipient, double value, Guild guild, long channelId) throws DataAccessException { if (value == 0) { log.error("Cannot make zero-value transactions"); return; @@ -86,6 +90,7 @@ public void performTransaction(long recipient, double value, Guild guild) throws HelpTransaction transaction = new HelpTransaction(); transaction.setRecipient(recipient); transaction.setWeight(value); + transaction.setChannelId(channelId); HelpAccount account = getOrCreateAccount(recipient); account.updateExperience(value); helpAccountRepository.update(account); @@ -109,4 +114,24 @@ private void checkExperienceRoles(@NotNull Guild guild, @NotNull HelpAccount acc } }), e -> {}); } + + /** + * add XP to all helpers depending on the messages they sent. + * + * @param post The {@link ThreadChannel} post + * @param allowIfXPAlreadyGiven {@code true} if XP should be awarded if XP have already been awarded + */ + public void addMessageBasedHelpXP(ThreadChannel post, boolean allowIfXPAlreadyGiven) { + HelpConfig config = botConfig.get(post.getGuild()).getHelpConfig(); + try { + Map experience = HelpManager.calculateExperience(HelpListener.HELP_POST_MESSAGES.get(post.getIdLong()), post.getOwnerIdLong(), config); + for (Map.Entry entry : experience.entrySet()) { + if(entry.getValue()>0 && (allowIfXPAlreadyGiven||!helpTransactionRepository.existsTransactionWithRecipientInChannel(entry.getKey(), post.getIdLong()))) { + performTransaction(entry.getKey(), entry.getValue(), config.getGuild(), post.getIdLong()); + } + } + } catch (DataAccessException e) { + ExceptionLogger.capture(e, getClass().getName()); + } + } } diff --git a/src/main/java/net/javadiscord/javabot/systems/help/HelpForumUpdater.java b/src/main/java/net/javadiscord/javabot/systems/help/HelpForumUpdater.java index 62fe9a2f0..5d02d13b2 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/HelpForumUpdater.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/HelpForumUpdater.java @@ -2,13 +2,18 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.JDA; 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.channel.concrete.ForumChannel; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.javadiscord.javabot.data.config.BotConfig; import net.javadiscord.javabot.data.config.guild.HelpConfig; +import net.javadiscord.javabot.systems.user_preferences.UserPreferenceService; +import net.javadiscord.javabot.systems.user_preferences.model.Preference; + import org.jetbrains.annotations.NotNull; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -24,6 +29,8 @@ public class HelpForumUpdater { private final JDA jda; private final BotConfig botConfig; + private final UserPreferenceService preferenceService; + private final HelpExperienceService experienceService; /** * Updates all help channels. @@ -61,13 +68,42 @@ private void checkForumPost(@NotNull ThreadChannel post, HelpConfig config) { } post.sendMessage(config.getDormantChannelMessageTemplate().formatted(config.getInactivityTimeoutMinutes())).queue(s -> { post.getManager().setArchived(true).queue(); + sendDMDormantInfoIfEnabled(post, config); + experienceService.addMessageBasedHelpXP(post, false); log.info("Archived forum thread '{}' (by {}) for inactivity (last message sent {} minutes ago)", post.getName(), post.getOwnerId(), minutesAgo); + }); } }, e -> log.error("Could not find latest message in forum thread {}:", post.getId(), e)); } + private void sendDMDormantInfoIfEnabled(ThreadChannel post, HelpConfig config) { + if(Boolean.parseBoolean(preferenceService.getOrCreate(post.getOwnerIdLong(), Preference.PRIVATE_DORMANT_NOTIFICATIONS).getState())) { + post + .getOwner() + .getUser() + .openPrivateChannel() + .flatMap(c -> c.sendMessageEmbeds(createDMDormantInfo(post, config))) + .queue(); + } + } + + private MessageEmbed createDMDormantInfo(ThreadChannel post, HelpConfig config) { + return new EmbedBuilder() + .setTitle("Post marked as dormant") + .setDescription( + config + .getDormantChannelPrivateMessageTemplate() + .formatted( + post.getAsMention(), + post.getParentChannel().getAsMention(), + config.getInactivityTimeoutMinutes(), + "https://discord.com/channels/" + post.getGuild().getId() + "/" + post.getId())) + .build() + ; + } + private boolean isThanksMessage(@NotNull Message m) { return m.getAuthor().isBot() && !m.getButtons().isEmpty() && m.getButtons().stream().allMatch(b -> b.getId() != null && b.getId().contains(HelpManager.HELP_THANKS_IDENTIFIER)); diff --git a/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java b/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java index 4d26a0f90..0c8a232a4 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java @@ -22,11 +22,9 @@ import net.javadiscord.javabot.data.h2db.DbActions; import net.javadiscord.javabot.systems.help.dao.HelpAccountRepository; import net.javadiscord.javabot.systems.help.dao.HelpTransactionRepository; -import net.javadiscord.javabot.util.ExceptionLogger; import net.javadiscord.javabot.util.InteractionUtils; import net.javadiscord.javabot.util.Responses; import org.jetbrains.annotations.NotNull; -import org.springframework.dao.DataAccessException; import xyz.dynxsty.dih4jda.interactions.components.ButtonHandler; import xyz.dynxsty.dih4jda.util.ComponentIdBuilder; @@ -48,7 +46,7 @@ public class HelpListener extends ListenerAdapter implements ButtonHandler { /** * A static Map that holds all messages that was sent in a specific reserved forum channel. */ - protected static final Map> HELP_POST_MESSAGES = new HashMap<>(); + static final Map> HELP_POST_MESSAGES = new HashMap<>(); private static final Set newThreadChannels; static { @@ -58,6 +56,7 @@ public class HelpListener extends ListenerAdapter implements ButtonHandler { private final BotConfig botConfig; private final HelpAccountRepository helpAccountRepository; private final HelpTransactionRepository helpTransactionRepository; + private final HelpExperienceService experienceService; private final DbActions dbActions; private final String[][] closeSuggestionDetectors = { {"close", "post"}, @@ -158,7 +157,6 @@ public void onChannelCreate(@NotNull ChannelCreateEvent event) { if (isInvalidForumPost(event.getChannel())) { return; } - HelpConfig config = botConfig.get(event.getGuild()).getHelpConfig(); ThreadChannel post = event.getChannel().asThreadChannel(); if (isInvalidHelpForumChannel(post.getParentChannel().asForumChannel())) { return; @@ -229,20 +227,11 @@ private void handleThanksCloseButton(@NotNull ButtonInteractionEvent event, Help event.getMessage().delete().queue(s -> { // close post manager.close(event, false, null); - // add experience - try { - HelpExperienceService service = new HelpExperienceService(botConfig, helpAccountRepository, helpTransactionRepository); - Map experience = HelpManager.calculateExperience(HELP_POST_MESSAGES.get(post.getIdLong()), post.getOwnerIdLong(), config); - for (Map.Entry entry : experience.entrySet()) { - service.performTransaction(entry.getKey(), entry.getValue(), config.getGuild()); - } - } catch (DataAccessException e) { - ExceptionLogger.capture(e, getClass().getName()); - } + experienceService.addMessageBasedHelpXP(post, true); // thank all helpers buttons.stream().filter(ActionComponent::isDisabled) .filter(b -> b.getId() != null) - .forEach(b -> manager.thankHelper(event, post, Long.parseLong(ComponentIdBuilder.split(b.getId())[2]))); + .forEach(b -> manager.thankHelper(event.getGuild(), post, Long.parseLong(ComponentIdBuilder.split(b.getId())[2]))); }); } diff --git a/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java b/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java index 0a2e1b120..a2ccf06e1 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java @@ -11,7 +11,6 @@ import net.dv8tion.jda.api.entities.UserSnowflake; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.entities.emoji.Emoji; -import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.interactions.Interaction; import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback; import net.dv8tion.jda.api.interactions.components.ActionRow; @@ -28,6 +27,7 @@ import net.javadiscord.javabot.util.Responses; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; + import xyz.dynxsty.dih4jda.util.ComponentIdBuilder; import java.sql.SQLException; @@ -120,16 +120,16 @@ public void close(UserSnowflake closedBy, @Nullable String reason) { postThread.sendMessageEmbeds(buildPostClosedEmbed(closedBy, reason)) .queue(s -> postThread.getManager().setArchived(true).queue()); } - + /** * Thanks a single user. * - * @param event The {@link ButtonInteractionEvent} that was fired. + * @param guild The {@link Guild} the helper is thanked in * @param postThread The {@link ThreadChannel} post. * @param helperId The helpers' discord id. */ - public void thankHelper(@NotNull ButtonInteractionEvent event, ThreadChannel postThread, long helperId) { - event.getJDA().retrieveUserById(helperId).queue(helper -> { + public void thankHelper(@NotNull Guild guild, ThreadChannel postThread, long helperId) { + guild.getJDA().retrieveUserById(helperId).queue(helper -> { // First insert the new thanks data. try { dbActions.update( @@ -139,13 +139,13 @@ public void thankHelper(@NotNull ButtonInteractionEvent event, ThreadChannel pos postThread.getIdLong(), helper.getIdLong() ); - HelpConfig config = botConfig.get(event.getGuild()).getHelpConfig(); + HelpConfig config = botConfig.get(guild).getHelpConfig(); HelpExperienceService service = new HelpExperienceService(botConfig, helpAccountRepository, helpTransactionRepository); // Perform experience transactions - service.performTransaction(helper.getIdLong(), config.getThankedExperience(), event.getGuild()); + service.performTransaction(helper.getIdLong(), config.getThankedExperience(), guild, postThread.getIdLong()); } catch (SQLException e) { ExceptionLogger.capture(e, getClass().getSimpleName()); - botConfig.get(event.getGuild()).getModerationConfig().getLogChannel().sendMessageFormat( + botConfig.get(guild).getModerationConfig().getLogChannel().sendMessageFormat( "Could not record user %s thanking %s for help in post %s: %s", postThread.getOwner().getUser().getAsTag(), helper.getAsTag(), diff --git a/src/main/java/net/javadiscord/javabot/systems/help/dao/HelpTransactionRepository.java b/src/main/java/net/javadiscord/javabot/systems/help/dao/HelpTransactionRepository.java index f1553badc..429d0e515 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/dao/HelpTransactionRepository.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/dao/HelpTransactionRepository.java @@ -34,12 +34,13 @@ public class HelpTransactionRepository { public HelpTransaction save(HelpTransaction transaction) throws DataAccessException { SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate) .withTableName("help_transaction") - .usingColumns("recipient","weight","messageType") + .usingColumns("recipient","weight","messageType", "channel") .usingGeneratedKeyColumns("id"); Number key = simpleJdbcInsert.executeAndReturnKey(Map.of( "recipient",transaction.getRecipient(), "weight",transaction.getWeight(), - "messageType",transaction.getMessageType()) + "messageType",transaction.getMessageType(), + "channel",transaction.getChannelId()) ); transaction.setId(key.longValue()); log.info("Inserted new Help Transaction: {}", transaction); @@ -80,6 +81,20 @@ private HelpTransaction read(ResultSet rs) throws SQLException { transaction.setCreatedAt(rs.getTimestamp("created_at").toLocalDateTime()); transaction.setWeight(rs.getDouble("weight")); transaction.setMessageType(rs.getInt("messageType")); + transaction.setChannelId(rs.getLong("channel")); return transaction; } + + /** + * Checks whether a transaction with a specific recipient exists in a specific channel. + * @param recipient The ID of the recipient + * @param channelId The ID of the channel + * @return {@code true} if it exists, else {@code false} + */ + public boolean existsTransactionWithRecipientInChannel(long recipient, long channelId) { + return jdbcTemplate.queryForObject( + "SELECT count(*) FROM help_transaction WHERE recipient=? AND channel = ?", + Integer.class, + recipient, channelId) > 0; + } } diff --git a/src/main/java/net/javadiscord/javabot/systems/help/model/HelpTransaction.java b/src/main/java/net/javadiscord/javabot/systems/help/model/HelpTransaction.java index c7db57cf8..fb9dbc756 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/model/HelpTransaction.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/model/HelpTransaction.java @@ -14,4 +14,5 @@ public class HelpTransaction { private LocalDateTime createdAt; private double weight; private int messageType; + private long channelId = -1; } diff --git a/src/main/java/net/javadiscord/javabot/systems/user_preferences/model/Preference.java b/src/main/java/net/javadiscord/javabot/systems/user_preferences/model/Preference.java index 1603ba7bc..c1edc35ef 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_preferences/model/Preference.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_preferences/model/Preference.java @@ -7,7 +7,11 @@ public enum Preference { /** * Enables/Disables QOTW reminders. */ - QOTW_REMINDER("Question of the Week Reminder", "false", new BooleanPreference()); + QOTW_REMINDER("Question of the Week Reminder", "false", new BooleanPreference()), + /** + * Enables/Disables DM notifications for dormant help post. + */ + PRIVATE_DORMANT_NOTIFICATIONS("Send notifications about dormant help post via DM", "true", new BooleanPreference()); private final String name; private final String defaultState; diff --git a/src/main/resources/database/migrations/07-08-2023_channel_transactions.sql b/src/main/resources/database/migrations/07-08-2023_channel_transactions.sql new file mode 100644 index 000000000..1c4a41538 --- /dev/null +++ b/src/main/resources/database/migrations/07-08-2023_channel_transactions.sql @@ -0,0 +1 @@ +ALTER TABLE help_transaction ADD COLUMN channel BIGINT DEFAULT -1; \ No newline at end of file From 8226111930e8f131f068227aa7f676fe26e1ccf6 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sat, 8 Jul 2023 16:32:46 +0200 Subject: [PATCH 018/351] regulate voting --- .../javabot/listener/SuggestionListener.java | 1 + .../listener/VotingRegulationListener.java | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/main/java/net/javadiscord/javabot/listener/VotingRegulationListener.java diff --git a/src/main/java/net/javadiscord/javabot/listener/SuggestionListener.java b/src/main/java/net/javadiscord/javabot/listener/SuggestionListener.java index f14dc1037..e42693230 100644 --- a/src/main/java/net/javadiscord/javabot/listener/SuggestionListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/SuggestionListener.java @@ -87,6 +87,7 @@ private MessageEmbed buildSuggestionEmbed(Message message) { .setColor(Responses.Type.DEFAULT.getColor()) .setTimestamp(Instant.now()) .setDescription(message.getContentRaw()) + .setFooter(message.getAuthor().getId()) .build(); } } diff --git a/src/main/java/net/javadiscord/javabot/listener/VotingRegulationListener.java b/src/main/java/net/javadiscord/javabot/listener/VotingRegulationListener.java new file mode 100644 index 000000000..2116f2ea6 --- /dev/null +++ b/src/main/java/net/javadiscord/javabot/listener/VotingRegulationListener.java @@ -0,0 +1,47 @@ +package net.javadiscord.javabot.listener; + +import lombok.RequiredArgsConstructor; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.javadiscord.javabot.data.config.BotConfig; + +/** + * Makes sure users don't vote on/star their own messages. + */ +@RequiredArgsConstructor +public class VotingRegulationListener extends ListenerAdapter{ + + private final BotConfig botConfig; + + @Override + public void onMessageReactionAdd(MessageReactionAddEvent event) { + if(isCriticalEmoji(event)) { + event.retrieveMessage().queue(msg->{ + if(doesAuthorMatch(event.getUserIdLong(), msg)) { + msg.removeReaction(event.getEmoji(), event.getUser()).queue(); + } + }); + } + } + + private boolean doesAuthorMatch(long userId, Message msg) { + long suggestionChannelId = botConfig.get(msg.getGuild()).getModerationConfig().getSuggestionChannelId(); + return msg.getAuthor().getIdLong()==userId|| + msg.getChannel().getIdLong() == suggestionChannelId && + !msg.getEmbeds().isEmpty() && + msg.getEmbeds().get(0).getFooter()!=null && + String.valueOf(userId).equals(msg.getEmbeds().get(0).getFooter().getText()); + } + + private boolean isCriticalEmoji(MessageReactionAddEvent event) { + return getUpvoteEmoji(event).equals(event.getEmoji()) || + event.getEmoji().getType() == Emoji.Type.UNICODE && + botConfig.get(event.getGuild()).getStarboardConfig().getEmojis().contains(event.getEmoji().asUnicode()); + } + + private Emoji getUpvoteEmoji(MessageReactionAddEvent event) { + return botConfig.getSystems().getEmojiConfig().getUpvoteEmote(event.getJDA()); + } +} From d9a3d8294f8bc86852562a7d1184fcaf8d712aa4 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sat, 8 Jul 2023 19:36:48 +0200 Subject: [PATCH 019/351] formatting --- .../javabot/listener/VotingRegulationListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/listener/VotingRegulationListener.java b/src/main/java/net/javadiscord/javabot/listener/VotingRegulationListener.java index 2116f2ea6..f90ef8c77 100644 --- a/src/main/java/net/javadiscord/javabot/listener/VotingRegulationListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/VotingRegulationListener.java @@ -28,10 +28,10 @@ public void onMessageReactionAdd(MessageReactionAddEvent event) { private boolean doesAuthorMatch(long userId, Message msg) { long suggestionChannelId = botConfig.get(msg.getGuild()).getModerationConfig().getSuggestionChannelId(); - return msg.getAuthor().getIdLong()==userId|| + return msg.getAuthor().getIdLong() == userId|| msg.getChannel().getIdLong() == suggestionChannelId && !msg.getEmbeds().isEmpty() && - msg.getEmbeds().get(0).getFooter()!=null && + msg.getEmbeds().get(0).getFooter() != null && String.valueOf(userId).equals(msg.getEmbeds().get(0).getFooter().getText()); } From 1ff40f6a308a6ea1531d4e7dd8a22a61ef37c163 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sat, 8 Jul 2023 22:08:28 +0200 Subject: [PATCH 020/351] XP plot --- .../help/commands/HelpAccountSubcommand.java | 79 +++++++++++++++++- .../help/dao/HelpTransactionRepository.java | 15 ++++ .../net/javadiscord/javabot/util/Plotter.java | 83 +++++++++++++++++++ 3 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/javadiscord/javabot/util/Plotter.java diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java index 92114955c..5042ad1a7 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java @@ -2,6 +2,7 @@ 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.User; @@ -9,13 +10,29 @@ import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; +import net.dv8tion.jda.api.requests.restaction.WebhookMessageCreateAction; +import net.dv8tion.jda.api.utils.FileUpload; +import net.javadiscord.javabot.data.config.BotConfig; import net.javadiscord.javabot.data.h2db.DbActions; import net.javadiscord.javabot.systems.help.HelpExperienceService; +import net.javadiscord.javabot.systems.help.dao.HelpTransactionRepository; import net.javadiscord.javabot.systems.help.model.HelpAccount; +import net.javadiscord.javabot.util.Checks; import net.javadiscord.javabot.util.ExceptionLogger; import net.javadiscord.javabot.util.Pair; +import net.javadiscord.javabot.util.Plotter; import net.javadiscord.javabot.util.Responses; import net.javadiscord.javabot.util.StringUtils; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +import javax.imageio.ImageIO; + import org.jetbrains.annotations.NotNull; import org.springframework.dao.DataAccessException; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; @@ -27,26 +44,40 @@ */ public class HelpAccountSubcommand extends SlashCommand.Subcommand { + private final BotConfig botConfig; private final DbActions dbActions; private final HelpExperienceService helpExperienceService; + private final HelpTransactionRepository transactionRepository; /** * The constructor of this class, which sets the corresponding {@link SubcommandData}. * + * @param botConfig The bot configuration * @param dbActions An object responsible for various database actions * @param helpExperienceService Service object that handles Help Experience Transactions. + * @param transactionRepository DAO for help XP transactions */ - public HelpAccountSubcommand(DbActions dbActions, HelpExperienceService helpExperienceService) { + public HelpAccountSubcommand(BotConfig botConfig, DbActions dbActions, HelpExperienceService helpExperienceService, HelpTransactionRepository transactionRepository) { this.dbActions = dbActions; this.helpExperienceService = helpExperienceService; + this.botConfig = botConfig; + this.transactionRepository = transactionRepository; setCommandData(new SubcommandData("account", "Shows an overview of your Help Account.") .addOption(OptionType.USER, "user", "If set, show the Help Account of the specified user instead.", false) + .addOption(OptionType.BOOLEAN, "plot", "generate a plot of help XP history", false) ); } @Override public void execute(@NotNull SlashCommandInteractionEvent event) { User user = event.getOption("user", event::getUser, OptionMapping::getAsUser); + boolean plot = event.getOption("plot", false, OptionMapping::getAsBoolean); + + if (plot && user.getIdLong()!=event.getUser().getIdLong() && !Checks.hasStaffRole(botConfig, event.getMember())) { + Responses.error(event, "You can only plot your own help XP history.").queue(); + return; + } + long totalThanks = dbActions.count( "SELECT COUNT(id) FROM help_channel_thanks WHERE helper_id = ?", s -> s.setLong(1, user.getIdLong()) @@ -55,15 +86,59 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { "SELECT COUNT(id) FROM help_channel_thanks WHERE helper_id = ? AND thanked_at > DATEADD('week', -1, CURRENT_TIMESTAMP(0))", s -> s.setLong(1, user.getIdLong()) ); + + event.deferReply().queue(); + + FileUpload upload = null; + if (plot) { + upload = generatePlot(user); + } + try { HelpAccount account = helpExperienceService.getOrCreateAccount(user.getIdLong()); - event.replyEmbeds(buildHelpAccountEmbed(account, user, event.getGuild(), totalThanks, weekThanks)).queue(); + WebhookMessageCreateAction reply = event.getHook().sendMessageEmbeds(buildHelpAccountEmbed(account, user, event.getGuild(), totalThanks, weekThanks)); + if (upload!=null) { + reply.addFiles(upload); + } + reply.queue(); } catch (DataAccessException e) { ExceptionLogger.capture(e, getClass().getSimpleName()); Responses.error(event, e.getMessage()).queue(); } } + private FileUpload generatePlot(User user) { + List,Double>> xpData = transactionRepository.getTotalTransactionWeightByMonth(user.getIdLong(), LocalDate.now().withDayOfMonth(1).minusYears(1).atStartOfDay()); + + if (xpData.isEmpty()) { + return null; + } + + List> plotData = new ArrayList<>(); + + int i = 0; + for(LocalDate position = LocalDate.now().minusYears(1); position.isBefore(LocalDate.now().plusDays(1)); position=position.plusMonths(1)) { + double value = 0.0; + if(i, Double> entry = xpData.get(i); + if(entry.first().first() == position.getMonthValue() && entry.first().second() == position.getYear()) { + value = Math.round(entry.second()*100)/100.0; + i++; + } + } + plotData.add(new Pair<>(position.getMonth() + " " + position.getYear(), value)); + } + + BufferedImage plt = new Plotter(plotData).plot(); + try(ByteArrayOutputStream os = new ByteArrayOutputStream()){ + ImageIO.write(plt, "png", os); + return FileUpload.fromData(os.toByteArray(), "image.png"); + } catch (IOException e) { + ExceptionLogger.capture(e, "Cannot create XP plot"); + } + return null; + } + private @NotNull MessageEmbed buildHelpAccountEmbed(HelpAccount account, @NotNull User user, Guild guild, long totalThanks, long weekThanks) { return new EmbedBuilder() .setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl()) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/dao/HelpTransactionRepository.java b/src/main/java/net/javadiscord/javabot/systems/help/dao/HelpTransactionRepository.java index f1553badc..a85479f01 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/dao/HelpTransactionRepository.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/dao/HelpTransactionRepository.java @@ -3,6 +3,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.javadiscord.javabot.systems.help.model.HelpTransaction; +import net.javadiscord.javabot.util.Pair; + import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; @@ -11,6 +13,7 @@ import java.sql.ResultSet; import java.sql.SQLException; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.Optional; @@ -82,4 +85,16 @@ private HelpTransaction read(ResultSet rs) throws SQLException { transaction.setMessageType(rs.getInt("messageType")); return transaction; } + + /** + * Gets the total earned XP of a user since a specific timestamp grouped by months. + * @param userId the user to get XP from + * @param start the start timestamp + * @return a list consisting of month, year and the total XP earned that month + */ + public List, Double>> getTotalTransactionWeightByMonth(long userId, LocalDateTime start) { + return jdbcTemplate.query("SELECT SUM(weight) AS total, EXTRACT(MONTH FROM created_at) AS m, EXTRACT(YEAR FROM created_at) AS y FROM help_transaction WHERE recipient = ? AND created_at >= ? GROUP BY m, y ORDER BY y ASC, m ASC", + (rs, row)-> new Pair<>(new Pair<>(rs.getInt("m"), rs.getInt("y")), rs.getDouble("total")), + userId, start); + } } diff --git a/src/main/java/net/javadiscord/javabot/util/Plotter.java b/src/main/java/net/javadiscord/javabot/util/Plotter.java new file mode 100644 index 000000000..84295bd00 --- /dev/null +++ b/src/main/java/net/javadiscord/javabot/util/Plotter.java @@ -0,0 +1,83 @@ +package net.javadiscord.javabot.util; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.util.List; + +/** + * Creates diagrams. + */ +public class Plotter { + private final List> entries; + private int width=3000; + private int height=1500; + + /** + * Creates the plotter. + * @param entries a list of all data points to plot, each represented as a {@link Pair} consisting of the name and value of the data point + */ + public Plotter(List> entries) { + this.entries=entries; + } + + /** + * Create a diagram from the data supplied to the constructor. + * @return the diagram as a {@link BufferedImage} + */ + public BufferedImage plot() { + BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = img.createGraphics(); + + g2d.setFont(ImageGenerationUtils.getResourceFont("assets/fonts/Uni-Sans-Heavy.ttf", 30).orElseThrow()); + + g2d.setBackground(Color.WHITE); + g2d.fillRect(0, 0, width, height); + g2d.setColor(Color.BLACK); + + centeredText(g2d, "gained help XP per month", width/2, 50); + + plotEntries(g2d, 100, 100, width-200, height-200); + + return img; + } + + private void plotEntries(Graphics2D g2d, int x, int y, int width, int height) { + double maxValue = entries.stream().mapToDouble(Pair::second).sum(); + int stepSize = 2*(int)Math.pow(10,(int)Math.log10(maxValue)-1); + if (stepSize==0) { + stepSize=1; + } + maxValue += stepSize; + + int numEntries = entries.size(); + + int currentX = x; + + g2d.drawLine(x, y, x, y+height); + + if(maxValue>0) { + for (int current = 0; current < maxValue; current += stepSize) { + g2d.drawString(String.valueOf(current), 95-g2d.getFontMetrics().stringWidth(String.valueOf(current)), this.height-(y+(height*current)/(int)maxValue)+g2d.getFontMetrics().getHeight()/3); + } + } + + boolean shift=false; + for (Pair entry : entries) { + int shiftNum = shift ? g2d.getFontMetrics().getHeight() : 0; + centeredText(g2d, entry.first(), currentX+(width/(2*numEntries)), this.height-y/2+shiftNum); + int entryHeight = (int)(height*entry.second()/maxValue); + g2d.setColor(Color.GRAY); + g2d.fillRect(currentX, this.height-y-entryHeight, width/numEntries, entryHeight); + g2d.setColor(Color.BLACK); + g2d.drawRect(currentX, this.height-y-entryHeight, width/numEntries, entryHeight); + centeredText(g2d, String.valueOf(entry.second()), currentX+(width/(2*numEntries)), this.height-y-entryHeight-10); + shift=!shift; + currentX += width/numEntries; + } + } + + private void centeredText(Graphics2D g2d, String text, int x, int y) { + g2d.drawString(text, x-g2d.getFontMetrics().stringWidth(text)/2, y); + } +} From 88dd63eaf2f0cbc9890198cd32f37ee4035d64f8 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sun, 9 Jul 2023 12:15:32 +0200 Subject: [PATCH 021/351] Generative AI and Java-specific info for QOTW --- .../net/javadiscord/javabot/systems/qotw/jobs/QOTWJob.java | 5 ++++- .../javabot/systems/qotw/submissions/SubmissionManager.java | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/qotw/jobs/QOTWJob.java b/src/main/java/net/javadiscord/javabot/systems/qotw/jobs/QOTWJob.java index d262af105..21c78c7ac 100644 --- a/src/main/java/net/javadiscord/javabot/systems/qotw/jobs/QOTWJob.java +++ b/src/main/java/net/javadiscord/javabot/systems/qotw/jobs/QOTWJob.java @@ -25,6 +25,8 @@ import java.util.Optional; import java.util.Set; +import javax.annotation.PostConstruct; + /** * Job which posts a new question to the QOTW channel. */ @@ -41,6 +43,7 @@ public class QOTWJob { * @throws SQLException if an SQL error occurs */ @Scheduled(cron = "0 0 9 * * 1") // Monday, 09:00 UTC + @PostConstruct public void execute() throws SQLException { for (Guild guild : jda.getGuilds()) { GuildConfig config = botConfig.get(guild); @@ -79,7 +82,7 @@ public void execute() throws SQLException { OffsetDateTime checkTime = OffsetDateTime.now().plusDays(6).withHour(22).withMinute(0).withSecond(0); return new EmbedBuilder() .setTitle("Question of the Week #" + question.getQuestionNumber()) - .setDescription(String.format("%s%n%nClick the button below to submit your answer.%nYour answer will be checked by ", + .setDescription(String.format("%s%n%nClick the button below to submit your answer.%nYour answer will be checked by %nUse of generative AI tools like ChatGPT is __not__ allowed", question.getText(), checkTime.toEpochSecond())) .build(); } diff --git a/src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionManager.java b/src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionManager.java index c8287252c..a9d8c171c 100644 --- a/src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionManager.java @@ -264,8 +264,10 @@ private CompletableFuture> getMessagesByUser(@NotNull ThreadChanne .addField("Note", """ To maximize your chances of getting this week's QOTW Point make sure to: - — Provide a **Code example** (if possible) - — Try to answer the question as detailed as possible. + - Provide a **Code example** (if possible) + - Try to answer the question as detailed as possible. + - Do not use generative AI tools like ChatGPT for answering the question. + - Make sure your answer is specific to Java. Staff usually won't reply in here.""", false) .setTimestamp(Instant.now()) From fc0ce9ed8b965fe8cac4bb0900835f685d4b80d5 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sun, 9 Jul 2023 12:17:44 +0200 Subject: [PATCH 022/351] remove invalid PostConstruct --- .../net/javadiscord/javabot/systems/qotw/jobs/QOTWJob.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/qotw/jobs/QOTWJob.java b/src/main/java/net/javadiscord/javabot/systems/qotw/jobs/QOTWJob.java index 21c78c7ac..9eef74702 100644 --- a/src/main/java/net/javadiscord/javabot/systems/qotw/jobs/QOTWJob.java +++ b/src/main/java/net/javadiscord/javabot/systems/qotw/jobs/QOTWJob.java @@ -25,8 +25,6 @@ import java.util.Optional; import java.util.Set; -import javax.annotation.PostConstruct; - /** * Job which posts a new question to the QOTW channel. */ @@ -43,7 +41,6 @@ public class QOTWJob { * @throws SQLException if an SQL error occurs */ @Scheduled(cron = "0 0 9 * * 1") // Monday, 09:00 UTC - @PostConstruct public void execute() throws SQLException { for (Guild guild : jda.getGuilds()) { GuildConfig config = botConfig.get(guild); From 5f393b8283acde44642b02cb4daac92453ec99e6 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sun, 9 Jul 2023 12:44:04 +0200 Subject: [PATCH 023/351] timeout users on automod --- .../javabot/systems/moderation/AutoMod.java | 58 ++++++------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/AutoMod.java b/src/main/java/net/javadiscord/javabot/systems/moderation/AutoMod.java index a93691e03..bd3217cba 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/AutoMod.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/AutoMod.java @@ -27,6 +27,7 @@ import java.util.Objects; import java.util.Scanner; import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -103,19 +104,6 @@ private boolean canBypassAutomod(Member member) { * @param message the {@link Message} that should be checked */ private void checkNewMessageAutomod(@Nonnull Message message) { - // mention spam - if (message.getMentions().getUsers().size() >= 5) { - new ModerationService(notificationService, botConfig.get(message.getGuild()), warnRepository, asyncPool) - .warn( - message.getAuthor(), - WarnSeverity.MEDIUM, - "Automod: Mention Spam", - message.getGuild().getMember(message.getJDA().getSelfUser()), - message.getChannel(), - false - ); - } - // spam message.getChannel().getHistory().retrievePast(10).queue(messages -> { int spamCount = (int) messages.stream().filter(msg -> !msg.equals(message)) @@ -138,39 +126,31 @@ private void checkNewMessageAutomod(@Nonnull Message message) { private void checkContentAutomod(@Nonnull Message message) { //Check for Advertising Links if (hasAdvertisingLink(message)) { - notificationService.withGuild(message.getGuild()).sendToModerationLog(c -> c.sendMessageFormat("Message: `%s`", message.getContentRaw())); - new ModerationService(notificationService, botConfig.get(message.getGuild()), warnRepository, asyncPool) - .warn( - message.getAuthor(), - WarnSeverity.MEDIUM, - "Automod: Advertising", - message.getGuild().getMember(message.getJDA().getSelfUser()), - message.getChannel(), - isSuggestionsChannel(message.getChannel()) - ); - message.delete().queue(success -> { - }, error -> log.info("Message was deleted before Automod was able to handle it.")); - - + doAutomodActions(message,"Advertising"); } //Check for suspicious Links if (hasSuspiciousLink(message)) { - notificationService.withGuild(message.getGuild()).sendToModerationLog(c -> c.sendMessageFormat("Suspicious Link sent by: %s (`%s`)", message.getAuthor().getAsMention(), message.getContentRaw())); - new ModerationService(notificationService, botConfig.get(message.getGuild()), warnRepository, asyncPool) - .warn( - message.getAuthor(), - WarnSeverity.MEDIUM, - "Automod: Suspicious Link", - message.getGuild().getMember(message.getJDA().getSelfUser()), - message.getChannel(), - isSuggestionsChannel(message.getChannel()) - ); - message.delete().queue(success -> { - }, error -> log.info("Message was deleted before Automod was able to handle it.")); + doAutomodActions(message, "Suspicious Link"); } } + private void doAutomodActions(Message message, String reason) { + notificationService.withGuild(message.getGuild()).sendToModerationLog(c -> c.sendMessageFormat("Message by %s: `%s`", message.getAuthor().getAsMention(), message.getContentRaw())); + new ModerationService(notificationService, botConfig.get(message.getGuild()), warnRepository, asyncPool) + .warn( + message.getAuthor(), + WarnSeverity.MEDIUM, + "Automod: " + reason, + message.getGuild().getMember(message.getJDA().getSelfUser()), + message.getChannel(), + isSuggestionsChannel(message.getChannel()) + ); + message.delete().queue(success -> { + }, error -> log.info("Message was deleted before Automod was able to handle it.")); + message.getMember().timeoutFor(30, TimeUnit.SECONDS).queue(); + } + /** * Handles potential spam messages. * From d7bc1d04c0f53845f083697d61e2cec63253109e Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sun, 9 Jul 2023 12:49:01 +0200 Subject: [PATCH 024/351] use getJumpUrl instead of manually encoding Discord URL --- .../net/javadiscord/javabot/systems/help/HelpForumUpdater.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/HelpForumUpdater.java b/src/main/java/net/javadiscord/javabot/systems/help/HelpForumUpdater.java index 5d02d13b2..f7c38d186 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/HelpForumUpdater.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/HelpForumUpdater.java @@ -99,7 +99,7 @@ private MessageEmbed createDMDormantInfo(ThreadChannel post, HelpConfig config) post.getAsMention(), post.getParentChannel().getAsMention(), config.getInactivityTimeoutMinutes(), - "https://discord.com/channels/" + post.getGuild().getId() + "/" + post.getId())) + post.getJumpUrl())) .build() ; } From 8aea9b2d018bf74a0c1d44caccff99fd220896ea Mon Sep 17 00:00:00 2001 From: danthe1st Date: Wed, 12 Jul 2023 22:41:24 +0200 Subject: [PATCH 025/351] externalize title of plot, use max for plot height --- .../systems/help/commands/HelpAccountSubcommand.java | 2 +- .../java/net/javadiscord/javabot/util/Plotter.java | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java index 5042ad1a7..18b01a667 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java @@ -129,7 +129,7 @@ private FileUpload generatePlot(User user) { plotData.add(new Pair<>(position.getMonth() + " " + position.getYear(), value)); } - BufferedImage plt = new Plotter(plotData).plot(); + BufferedImage plt = new Plotter(plotData, "gained help XP per month").plot(); try(ByteArrayOutputStream os = new ByteArrayOutputStream()){ ImageIO.write(plt, "png", os); return FileUpload.fromData(os.toByteArray(), "image.png"); diff --git a/src/main/java/net/javadiscord/javabot/util/Plotter.java b/src/main/java/net/javadiscord/javabot/util/Plotter.java index 84295bd00..5f648aeeb 100644 --- a/src/main/java/net/javadiscord/javabot/util/Plotter.java +++ b/src/main/java/net/javadiscord/javabot/util/Plotter.java @@ -9,6 +9,7 @@ * Creates diagrams. */ public class Plotter { + private String title; private final List> entries; private int width=3000; private int height=1500; @@ -16,9 +17,11 @@ public class Plotter { /** * Creates the plotter. * @param entries a list of all data points to plot, each represented as a {@link Pair} consisting of the name and value of the data point + * @param title the title of the plot */ - public Plotter(List> entries) { - this.entries=entries; + public Plotter(List> entries, String title) { + this.entries = entries; + this.title = title; } /** @@ -35,7 +38,7 @@ public BufferedImage plot() { g2d.fillRect(0, 0, width, height); g2d.setColor(Color.BLACK); - centeredText(g2d, "gained help XP per month", width/2, 50); + centeredText(g2d, title, width/2, 50); plotEntries(g2d, 100, 100, width-200, height-200); @@ -43,7 +46,7 @@ public BufferedImage plot() { } private void plotEntries(Graphics2D g2d, int x, int y, int width, int height) { - double maxValue = entries.stream().mapToDouble(Pair::second).sum(); + double maxValue = entries.stream().mapToDouble(Pair::second).max().orElse(0); int stepSize = 2*(int)Math.pow(10,(int)Math.log10(maxValue)-1); if (stepSize==0) { stepSize=1; From 6c4cf74ea2baead075978f4b29aaee847e48b77e Mon Sep 17 00:00:00 2001 From: danthe1st Date: Wed, 12 Jul 2023 22:51:34 +0200 Subject: [PATCH 026/351] use embed image instead of external attachment --- .../help/commands/HelpAccountSubcommand.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java index 18b01a667..4506dba31 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java @@ -96,7 +96,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { try { HelpAccount account = helpExperienceService.getOrCreateAccount(user.getIdLong()); - WebhookMessageCreateAction reply = event.getHook().sendMessageEmbeds(buildHelpAccountEmbed(account, user, event.getGuild(), totalThanks, weekThanks)); + WebhookMessageCreateAction reply = event.getHook().sendMessageEmbeds(buildHelpAccountEmbed(account, user, event.getGuild(), totalThanks, weekThanks, upload)); if (upload!=null) { reply.addFiles(upload); } @@ -139,16 +139,19 @@ private FileUpload generatePlot(User user) { return null; } - private @NotNull MessageEmbed buildHelpAccountEmbed(HelpAccount account, @NotNull User user, Guild guild, long totalThanks, long weekThanks) { - return new EmbedBuilder() + private @NotNull MessageEmbed buildHelpAccountEmbed(HelpAccount account, @NotNull User user, Guild guild, long totalThanks, long weekThanks, FileUpload upload) { + EmbedBuilder eb = new EmbedBuilder() .setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl()) .setTitle("Help Account") .setThumbnail(user.getEffectiveAvatarUrl()) .setDescription("Here are some statistics about how you've helped others here.") .addField("Experience (BETA)", formatExperience(guild, account), false) .addField("Total Times Thanked", String.format("**%s**", totalThanks), true) - .addField("Times Thanked This Week", String.format("**%s**", weekThanks), true) - .build(); + .addField("Times Thanked This Week", String.format("**%s**", weekThanks), true); + if (upload != null) { + eb.setImage("attachment://"+upload.getName()); + } + return eb.build(); } private @NotNull String formatExperience(Guild guild, @NotNull HelpAccount account) { From 8cafb410e7bf4c85f9b038ab30befd4b23b8ffac Mon Sep 17 00:00:00 2001 From: danthe1st Date: Fri, 21 Jul 2023 17:29:13 +0200 Subject: [PATCH 027/351] send help-ping info in distinct channel without ping --- .../javabot/data/config/guild/HelpConfig.java | 7 +- .../help/commands/HelpPingSubcommand.java | 143 ++++++++++++++---- 2 files changed, 116 insertions(+), 34 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java b/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java index 760d27524..268a4bd08 100644 --- a/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java +++ b/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java @@ -4,6 +4,7 @@ import lombok.EqualsAndHashCode; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.javadiscord.javabot.data.config.GuildConfigItem; import java.util.Map; @@ -24,7 +25,7 @@ public class HelpConfig extends GuildConfigItem { /** * The id of the help-ping role. */ - private long helpPingRoleId; + private long helpNotificationChannelId; /** * The message that's sent as soon as a user asks a question in an open help @@ -108,7 +109,7 @@ public Role getHelperRole() { return getGuild().getRoleById(helperRoleId); } - public Role getHelpPingRole() { - return getGuild().getRoleById(helpPingRoleId); + public TextChannel getHelpNotificationChannel() { + return getGuild().getTextChannelById(helpNotificationChannelId); } } diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java index f015415cc..09b4be502 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java @@ -1,24 +1,32 @@ package net.javadiscord.javabot.systems.help.commands; -import net.dv8tion.jda.api.OnlineStatus; +import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; +import net.dv8tion.jda.api.entities.channel.forums.ForumTag; +import net.dv8tion.jda.api.entities.emoji.EmojiUnion; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; +import net.javadiscord.javabot.annotations.AutoDetectableComponentHandler; import net.javadiscord.javabot.data.config.BotConfig; import net.javadiscord.javabot.data.config.GuildConfig; import net.javadiscord.javabot.data.config.guild.HelpConfig; -import net.javadiscord.javabot.util.ExceptionLogger; import net.javadiscord.javabot.util.Pair; import net.javadiscord.javabot.util.Responses; import org.jetbrains.annotations.NotNull; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; +import xyz.dynxsty.dih4jda.interactions.components.ButtonHandler; +import xyz.dynxsty.dih4jda.util.ComponentIdBuilder; +import java.awt.Color; import java.time.OffsetDateTime; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -31,7 +39,8 @@ * Handler for the /help ping sub-command that allows users to occasionally ping * helpers. */ -public class HelpPingSubcommand extends SlashCommand.Subcommand { +@AutoDetectableComponentHandler("help-ping") +public class HelpPingSubcommand extends SlashCommand.Subcommand implements ButtonHandler { private static final String WRONG_CHANNEL_MSG = "This command can only be used in **help forum posts**"; private static final long CACHE_CLEANUP_DELAY = 60L; @@ -80,38 +89,61 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { } if (isHelpPingTimeoutElapsed(member.getIdLong(), config)) { lastPingTimes.put(event.getMember().getIdLong(), new Pair<>(System.currentTimeMillis(), config.getGuild())); - Role role = config.getHelpConfig().getHelpPingRole(); - role.getGuild().findMembersWithRoles(role).onSuccess(members->{ - post.retrieveThreadMembers().queue(threadMembers -> { - Set memberIds = threadMembers - .stream() - .map(m->m.getIdLong()) - .collect(Collectors.toSet()); - members.removeIf(m -> - m.getOnlineStatus() == OnlineStatus.OFFLINE || - memberIds.contains(m.getIdLong())); - Collections.shuffle(members); - if(members.size()>0) { - post.addThreadMember(members.get(0)).queue(); - event.getHook().sendMessage("Successfully added a user with the help-ping role to the post.").queue(); - }else { - event.getHook().sendMessage("Unfortunately, no available member with the help-ping role has been found.").queue(); - } - }, err -> { - event.getHook().sendMessage("An error occured trying to find available members").queue(); - ExceptionLogger.capture(err, HelpPingSubcommand.class.getName()); - }); - - }).onError(err -> { - event.getHook().sendMessage("An error occured trying to find available members").queue(); - ExceptionLogger.capture(err, HelpPingSubcommand.class.getName()); - }); - event.deferReply(false).queue(); + TextChannel notifChannel = config.getHelpConfig().getHelpNotificationChannel(); + notifChannel.sendMessageEmbeds(new EmbedBuilder().setDescription(""" + %s requested help in post %s + + Tags: + %s + + [Post link](%s) + """ + .formatted( + event.getUser().getAsMention(), + post.getAsMention(), + getTagString(post), + post.getJumpUrl() + )) + .setAuthor(member.getEffectiveName(), null, member.getEffectiveAvatarUrl()) + .setFooter(event.getUser().getId()) + .setColor(Color.YELLOW) + .build()) + .addActionRow(createResolveButton()) + .queue(); + event.reply(""" + Successfully requested help. + + Note that this does NOT gurantee that anybody here has the time and knowledge to help you. + Abusing this command might result in moderative action taken against you. + """) + .setEphemeral(true) + .queue(); } else { Responses.warning(event, "Sorry, but you can only use this command occasionally. Please try again later.").queue(); } } + private String getTagString(ThreadChannel post) { + String text = post + .getAppliedTags() + .stream() + .map(this::getForumTagText) + .map(tag -> "- " + tag) + .collect(Collectors.joining("\n")); + if(text.isEmpty()) { + text = "- "; + } + return text; + } + + private Button createResolveButton() { + return Button.of(ButtonStyle.SECONDARY, ComponentIdBuilder.build("help-ping", "resolve"), "Mark as resolved"); + } + + private Button createUnresolveButton() { + return Button.of(ButtonStyle.SECONDARY, ComponentIdBuilder.build("help-ping", "unresolve"), "Mark as unresolved"); + } + /** * Determines if a user is forbidden from sending a help-ping command due * to their status in the server. @@ -161,4 +193,53 @@ private void cleanTimeoutCache() { lastPingTimes.remove(memberId); } } + + @Override + public void handleButton(ButtonInteractionEvent event, Button button) { + String[] id = ComponentIdBuilder.split(event.getComponentId()); + switch(id[1]) { + case "resolve" -> + resolveAction(event, true); + case "unresolve" -> + resolveAction(event, false); + default -> event.reply("Unknown button").setEphemeral(true).queue(); + } + + } + + private void resolveAction(ButtonInteractionEvent event, boolean resolved) { + event.editMessageEmbeds( + event.getMessage() + .getEmbeds() + .stream() + .map(e->new EmbedBuilder(e) + .setColor(resolved ? Color.GREEN : Color.YELLOW) + .addField("marked as " + (resolved?"resolved":"unresolved") + " by", + event.getUser().getAsMention(), false)) + .map(this::removeOldField) + .map(EmbedBuilder::build) + .toList()) + .setActionRow(resolved?createUnresolveButton():createResolveButton()) + .queue(); + } + + private String getForumTagText(ForumTag tag) { + EmojiUnion emoji = tag.getEmoji(); + StringBuilder sb=new StringBuilder(); + if(emoji!=null) { + sb + .append(emoji.getFormatted()) + .append(" "); + } + sb.append(tag.getName()); + + return sb.toString(); + } + + private EmbedBuilder removeOldField(EmbedBuilder eb) { + if(eb.getFields().size()>5) { + eb.getFields().remove(0); + } + return eb; + } } From f816377e8351eb56654a438d12d0515c5a768ef6 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Fri, 21 Jul 2023 21:52:48 +0200 Subject: [PATCH 028/351] implement suggested changes --- .../javabot/systems/help/commands/HelpPingSubcommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java index 09b4be502..3625dbd81 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java @@ -91,12 +91,12 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { lastPingTimes.put(event.getMember().getIdLong(), new Pair<>(System.currentTimeMillis(), config.getGuild())); TextChannel notifChannel = config.getHelpConfig().getHelpNotificationChannel(); notifChannel.sendMessageEmbeds(new EmbedBuilder().setDescription(""" - %s requested help in post %s + %s requested help in %s Tags: %s - [Post link](%s) + [Click to view](%s) """ .formatted( event.getUser().getAsMention(), From 511dbdcd692a046f3a34cf82464ead62d725e636 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sat, 22 Jul 2023 19:00:20 +0200 Subject: [PATCH 029/351] change button name for help ping embed --- .../systems/help/commands/HelpPingSubcommand.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java index 3625dbd81..44c5533a5 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java @@ -137,11 +137,11 @@ private String getTagString(ThreadChannel post) { } private Button createResolveButton() { - return Button.of(ButtonStyle.SECONDARY, ComponentIdBuilder.build("help-ping", "resolve"), "Mark as resolved"); + return Button.of(ButtonStyle.SECONDARY, ComponentIdBuilder.build("help-ping", "acknowledge"), "Mark as resolved"); } private Button createUnresolveButton() { - return Button.of(ButtonStyle.SECONDARY, ComponentIdBuilder.build("help-ping", "unresolve"), "Mark as unresolved"); + return Button.of(ButtonStyle.SECONDARY, ComponentIdBuilder.build("help-ping", "unacknowledge"), "Mark as unresolved"); } /** @@ -198,9 +198,9 @@ private void cleanTimeoutCache() { public void handleButton(ButtonInteractionEvent event, Button button) { String[] id = ComponentIdBuilder.split(event.getComponentId()); switch(id[1]) { - case "resolve" -> + case "acknowledge" -> resolveAction(event, true); - case "unresolve" -> + case "unacknowledge" -> resolveAction(event, false); default -> event.reply("Unknown button").setEphemeral(true).queue(); } @@ -213,8 +213,8 @@ private void resolveAction(ButtonInteractionEvent event, boolean resolved) { .getEmbeds() .stream() .map(e->new EmbedBuilder(e) - .setColor(resolved ? Color.GREEN : Color.YELLOW) - .addField("marked as " + (resolved?"resolved":"unresolved") + " by", + .setColor(resolved ? Color.GRAY : Color.YELLOW) + .addField("marked as " + (resolved?"acknowledged":"needs help") + " by", event.getUser().getAsMention(), false)) .map(this::removeOldField) .map(EmbedBuilder::build) From a14fe10d64fde297c4c817a203e0ec6448dba6d7 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sat, 22 Jul 2023 19:02:26 +0200 Subject: [PATCH 030/351] finish up renaming from reserve to acknowledge --- .../help/commands/HelpPingSubcommand.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java index 44c5533a5..e6d2eac16 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java @@ -108,7 +108,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { .setFooter(event.getUser().getId()) .setColor(Color.YELLOW) .build()) - .addActionRow(createResolveButton()) + .addActionRow(createAcknowledgementButton()) .queue(); event.reply(""" Successfully requested help. @@ -136,12 +136,12 @@ private String getTagString(ThreadChannel post) { return text; } - private Button createResolveButton() { - return Button.of(ButtonStyle.SECONDARY, ComponentIdBuilder.build("help-ping", "acknowledge"), "Mark as resolved"); + private Button createAcknowledgementButton() { + return Button.of(ButtonStyle.SECONDARY, ComponentIdBuilder.build("help-ping", "acknowledge"), "Mark as acknowledged"); } - private Button createUnresolveButton() { - return Button.of(ButtonStyle.SECONDARY, ComponentIdBuilder.build("help-ping", "unacknowledge"), "Mark as unresolved"); + private Button createUndoAcknowledgementButton() { + return Button.of(ButtonStyle.SECONDARY, ComponentIdBuilder.build("help-ping", "unacknowledge"), "Mark as unacknowledged"); } /** @@ -207,19 +207,19 @@ public void handleButton(ButtonInteractionEvent event, Button button) { } - private void resolveAction(ButtonInteractionEvent event, boolean resolved) { + private void resolveAction(ButtonInteractionEvent event, boolean acknowledged) { event.editMessageEmbeds( event.getMessage() .getEmbeds() .stream() .map(e->new EmbedBuilder(e) - .setColor(resolved ? Color.GRAY : Color.YELLOW) - .addField("marked as " + (resolved?"acknowledged":"needs help") + " by", + .setColor(acknowledged ? Color.GRAY : Color.YELLOW) + .addField("marked as " + (acknowledged?"acknowledged":"needs help") + " by", event.getUser().getAsMention(), false)) .map(this::removeOldField) .map(EmbedBuilder::build) .toList()) - .setActionRow(resolved?createUnresolveButton():createResolveButton()) + .setActionRow(acknowledged?createUndoAcknowledgementButton():createAcknowledgementButton()) .queue(); } From 3b6cd90191b9b9201159fc344af68a845379cb0b Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sat, 22 Jul 2023 19:02:45 +0200 Subject: [PATCH 031/351] change method name --- .../javabot/systems/help/commands/HelpPingSubcommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java index e6d2eac16..ec5b26361 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java @@ -199,15 +199,15 @@ public void handleButton(ButtonInteractionEvent event, Button button) { String[] id = ComponentIdBuilder.split(event.getComponentId()); switch(id[1]) { case "acknowledge" -> - resolveAction(event, true); + acknowledgeChangeAction(event, true); case "unacknowledge" -> - resolveAction(event, false); + acknowledgeChangeAction(event, false); default -> event.reply("Unknown button").setEphemeral(true).queue(); } } - private void resolveAction(ButtonInteractionEvent event, boolean acknowledged) { + private void acknowledgeChangeAction(ButtonInteractionEvent event, boolean acknowledged) { event.editMessageEmbeds( event.getMessage() .getEmbeds() From a3bbe895a8aa38fdf9819fc9a109bcef70627e52 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Mon, 24 Jul 2023 11:31:37 +0200 Subject: [PATCH 032/351] add DM when post is closed by other user, fix preferences --- .../javabot/listener/UserLeaveListener.java | 4 ++- .../javabot/systems/help/HelpListener.java | 4 ++- .../javabot/systems/help/HelpManager.java | 30 +++++++++++++++++++ .../systems/help/commands/CloseCommand.java | 6 ++-- .../help/commands/UnreserveCommand.java | 18 ++++++----- .../commands/PreferencesSetSubcommand.java | 13 ++++---- .../user_preferences/model/Preference.java | 9 ++++-- 7 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/listener/UserLeaveListener.java b/src/main/java/net/javadiscord/javabot/listener/UserLeaveListener.java index 41ffae8af..195b4bd43 100644 --- a/src/main/java/net/javadiscord/javabot/listener/UserLeaveListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/UserLeaveListener.java @@ -16,6 +16,7 @@ import net.javadiscord.javabot.systems.help.HelpManager; import net.javadiscord.javabot.systems.help.dao.HelpAccountRepository; import net.javadiscord.javabot.systems.help.dao.HelpTransactionRepository; +import net.javadiscord.javabot.systems.user_preferences.UserPreferenceService; import java.util.function.Consumer; @@ -31,6 +32,7 @@ public class UserLeaveListener extends ListenerAdapter { private final BotConfig botConfig; private final HelpAccountRepository helpAccountRepository; private final HelpTransactionRepository helpTransactionRepository; + private final UserPreferenceService userPreferenceService; @Override public void onGuildMemberRemove(@NotNull GuildMemberRemoveEvent event) { @@ -84,7 +86,7 @@ private void executeForAllOpenPostsOfUser(Guild guild, User user, ForumChannel f } private void unreserveHelpChannel(ThreadChannel post) { - HelpManager manager = new HelpManager(post, dbActions, botConfig, helpAccountRepository, helpTransactionRepository); + HelpManager manager = new HelpManager(post, dbActions, botConfig, helpAccountRepository, helpTransactionRepository, userPreferenceService); manager.close(UserSnowflake.fromId(post.getGuild().getSelfMember().getIdLong()), "User left the server"); } } diff --git a/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java b/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java index 0c8a232a4..e77f8e2ec 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java @@ -22,6 +22,7 @@ import net.javadiscord.javabot.data.h2db.DbActions; import net.javadiscord.javabot.systems.help.dao.HelpAccountRepository; import net.javadiscord.javabot.systems.help.dao.HelpTransactionRepository; +import net.javadiscord.javabot.systems.user_preferences.UserPreferenceService; import net.javadiscord.javabot.util.InteractionUtils; import net.javadiscord.javabot.util.Responses; import org.jetbrains.annotations.NotNull; @@ -73,6 +74,7 @@ protected boolean removeEldestEntry(Map.Entry eldest) { return System.currentTimeMillis() > eldest.getValue() || size() >= 32; } }; + private final UserPreferenceService preferenceService; @Override public void onMessageReceived(@NotNull MessageReceivedEvent event) { @@ -176,7 +178,7 @@ public void handleButton(@NotNull ButtonInteractionEvent event, @NotNull Button return; } ThreadChannel post = event.getChannel().asThreadChannel(); - HelpManager manager = new HelpManager(post, dbActions, botConfig, helpAccountRepository, helpTransactionRepository); + HelpManager manager = new HelpManager(post, dbActions, botConfig, helpAccountRepository, helpTransactionRepository, preferenceService); switch (id[0]) { case HelpManager.HELP_THANKS_IDENTIFIER -> handleHelpThanksInteraction(event, manager, id); case HelpManager.HELP_GUIDELINES_IDENTIFIER -> diff --git a/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java b/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java index a2ccf06e1..476c5be79 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java @@ -9,6 +9,7 @@ import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.UserSnowflake; +import net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.interactions.Interaction; @@ -16,12 +17,15 @@ import net.dv8tion.jda.api.interactions.components.ActionRow; import net.dv8tion.jda.api.interactions.components.ItemComponent; import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import net.javadiscord.javabot.data.config.BotConfig; import net.javadiscord.javabot.data.config.guild.HelpConfig; import net.javadiscord.javabot.data.h2db.DbActions; import net.javadiscord.javabot.systems.help.dao.HelpAccountRepository; import net.javadiscord.javabot.systems.help.dao.HelpTransactionRepository; +import net.javadiscord.javabot.systems.user_preferences.UserPreferenceService; +import net.javadiscord.javabot.systems.user_preferences.model.Preference; import net.javadiscord.javabot.util.ExceptionLogger; import net.javadiscord.javabot.util.MessageActionUtils; import net.javadiscord.javabot.util.Responses; @@ -64,6 +68,7 @@ public class HelpManager { private final HelpAccountRepository helpAccountRepository; private final HelpTransactionRepository helpTransactionRepository; + private final UserPreferenceService preferenceService; /** * Builds and replies {@link ActionRow}s with all members which helped the @@ -108,6 +113,31 @@ public void close(IReplyCallback callback, boolean withHelpers, @Nullable String Responses.info(callback, "Post Closed", "This post has been closed by %s%s", callback.getUser().getAsMention(), reason != null ? " for the following reason:\n> " + reason : ".") .setEphemeral(false) .queue(s -> postThread.getManager().setLocked(true).setArchived(true).queue()); + if (callback.getMember().getIdLong() != postThread.getOwnerIdLong() && + Boolean.parseBoolean(preferenceService.getOrCreate(postThread.getOwnerIdLong(), Preference.PRIVATE_CLOSE_NOTIFICATIONS).getState())) { + postThread.getOwner().getUser().openPrivateChannel() + .flatMap(c -> createDMCloseInfoEmbed(callback.getMember(), postThread, reason, c)) + .queue(success -> {}, failure -> {}); + } + } + + private RestAction createDMCloseInfoEmbed(Member closingMember, ThreadChannel postThread, String reason, PrivateChannel c) { + EmbedBuilder eb = new EmbedBuilder() + .setTitle("Post closed") + .setDescription( + """ + Your post %s has been closed by %s + + [Post link](%s) + """ + .formatted(postThread.getAsMention(), closingMember.getAsMention(), postThread.getJumpUrl())) + .addField("Deactivate", "You can deactivate notifications like this using the `/preferences` command.", false) + .setAuthor(closingMember.getEffectiveName(), null, closingMember.getEffectiveAvatarUrl()); + if (reason != null) { + eb + .addField("Reason", reason, false); + } + return c.sendMessageEmbeds(eb.build()); } /** diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/CloseCommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/CloseCommand.java index fbe510ad9..c3a4eaf56 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/CloseCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/CloseCommand.java @@ -6,6 +6,7 @@ import net.javadiscord.javabot.data.h2db.DbActions; import net.javadiscord.javabot.systems.help.dao.HelpAccountRepository; import net.javadiscord.javabot.systems.help.dao.HelpTransactionRepository; +import net.javadiscord.javabot.systems.user_preferences.UserPreferenceService; /** * A simple command that can be used inside reserved help channels to immediately unreserve them, @@ -21,9 +22,10 @@ public class CloseCommand extends UnreserveCommand { * @param dbActions A utility object providing various operations on the main database * @param helpTransactionRepository Dao object that represents the HELP_TRANSACTION SQL Table. * @param helpAccountRepository Dao object that represents the HELP_ACCOUNT SQL Table. + * @param preferenceService Service for user preferences */ - public CloseCommand(BotConfig botConfig, DbActions dbActions, HelpTransactionRepository helpTransactionRepository, HelpAccountRepository helpAccountRepository) { - super(botConfig, dbActions, helpTransactionRepository, helpAccountRepository); + public CloseCommand(BotConfig botConfig, DbActions dbActions, HelpTransactionRepository helpTransactionRepository, HelpAccountRepository helpAccountRepository, UserPreferenceService preferenceService) { + super(botConfig, dbActions, helpTransactionRepository, helpAccountRepository, preferenceService); setCommandData( Commands.slash("close", "Unreserves this post marking your question/issue as resolved.") .setGuildOnly(true).addOption(OptionType.STRING, "reason", diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/UnreserveCommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/UnreserveCommand.java index 4d2e1e7a8..cee4db0ec 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/UnreserveCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/UnreserveCommand.java @@ -11,6 +11,7 @@ import net.javadiscord.javabot.data.h2db.DbActions; import net.javadiscord.javabot.systems.help.dao.HelpAccountRepository; import net.javadiscord.javabot.systems.help.dao.HelpTransactionRepository; +import net.javadiscord.javabot.systems.user_preferences.UserPreferenceService; import net.javadiscord.javabot.systems.help.HelpManager; import net.javadiscord.javabot.util.Responses; import org.jetbrains.annotations.NotNull; @@ -25,19 +26,22 @@ public class UnreserveCommand extends SlashCommand { private final DbActions dbActions; private final HelpAccountRepository helpAccountRepository; private final HelpTransactionRepository helpTransactionRepository; + private final UserPreferenceService preferenceService; /** * The constructor of this class, which sets the corresponding {@link net.dv8tion.jda.api.interactions.commands.build.SlashCommandData}. * @param botConfig The main configuration of the bot * @param dbActions A utility object providing various operations on the main database - * @param helpTransactionRepository Dao object that represents the HELP_TRANSACTION SQL Table. - * @param helpAccountRepository Dao object that represents the HELP_ACCOUNT SQL Table. + * @param helpTransactionRepository Dao object that represents the HELP_TRANSACTION SQL Table + * @param helpAccountRepository Dao object that represents the HELP_ACCOUNT SQL Table + * @param preferenceService Service for user preferences */ - public UnreserveCommand(BotConfig botConfig, DbActions dbActions, HelpTransactionRepository helpTransactionRepository, HelpAccountRepository helpAccountRepository) { + public UnreserveCommand(BotConfig botConfig, DbActions dbActions, HelpTransactionRepository helpTransactionRepository, HelpAccountRepository helpAccountRepository, UserPreferenceService preferenceService) { this.botConfig = botConfig; this.dbActions = dbActions; this.helpAccountRepository = helpAccountRepository; this.helpTransactionRepository = helpTransactionRepository; + this.preferenceService = preferenceService; setCommandData(Commands.slash("unreserve", "Unreserves this post marking your question/issue as resolved.") .setGuildOnly(true) .addOption(OptionType.STRING, "reason", "The reason why you're unreserving this channel", false) @@ -55,11 +59,11 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { if (postThread.getParentChannel().getType() != ChannelType.FORUM) { replyInvalidChannel(event); } - HelpManager manager = new HelpManager(postThread, dbActions, botConfig, helpAccountRepository, helpTransactionRepository); + HelpManager manager = new HelpManager(postThread, dbActions, botConfig, helpAccountRepository, helpTransactionRepository, preferenceService); if (manager.isForumEligibleToBeUnreserved(event.getInteraction())) { - manager.close(event, event.getUser().getIdLong() == postThread.getOwnerIdLong(), - event.getOption("reason", null, OptionMapping::getAsString) - ); + manager.close(event, + event.getUser().getIdLong() == manager.getPostThread().getOwnerIdLong(), + event.getOption("reason", null, OptionMapping::getAsString)); } else { Responses.warning(event, "Could not close this post", "You're not allowed to close this post.").queue(); } diff --git a/src/main/java/net/javadiscord/javabot/systems/user_preferences/commands/PreferencesSetSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/user_preferences/commands/PreferencesSetSubcommand.java index cd4f31f4c..1306bef1f 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_preferences/commands/PreferencesSetSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_preferences/commands/PreferencesSetSubcommand.java @@ -17,6 +17,7 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -58,7 +59,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { return; } if (service.setOrCreate(event.getUser().getIdLong(), preference, state)) { - Responses.info(event, "Preference Updated", "Successfully set %s to `%s`!", preference, state).queue(); + Responses.info(event, "Preference Updated", "Successfully set `%s` to `%s`!", preference, state).queue(); } else { Responses.error(event, "Could not set %s to `%s`.", preference, state).queue(); } @@ -66,16 +67,16 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { @Contract("_ -> new") private Command.@NotNull Choice toChoice(@NotNull Preference preference) { - return new Command.Choice(preference.toString(), String.valueOf(preference.ordinal())); + return new Command.Choice(preference.toString(), preference.ordinal()); } @Override public void handleAutoComplete(@NotNull CommandAutoCompleteInteractionEvent event, @NotNull AutoCompleteQuery target) { - String preferenceString = event.getOption("preference", OptionMapping::getAsString); - if (preferenceString != null && Arrays.stream(Preference.values()).map(Preference::name).anyMatch(c -> c.equals(preferenceString))) { - Preference preference = Preference.valueOf(preferenceString); + int preferenceInt = event.getOption("preference",-1, OptionMapping::getAsInt); + if (preferenceInt > 0 && preferenceInt 0) { - event.replyChoices(AutoCompleteUtils.filterChoices(event, List.of(preference.getType().getDefaultChoices()))).queue(); + event.replyChoices(AutoCompleteUtils.filterChoices(event, new ArrayList<>(List.of(preference.getType().getDefaultChoices())))).queue(); } } } diff --git a/src/main/java/net/javadiscord/javabot/systems/user_preferences/model/Preference.java b/src/main/java/net/javadiscord/javabot/systems/user_preferences/model/Preference.java index c1edc35ef..4135c60c8 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_preferences/model/Preference.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_preferences/model/Preference.java @@ -9,9 +9,14 @@ public enum Preference { */ QOTW_REMINDER("Question of the Week Reminder", "false", new BooleanPreference()), /** - * Enables/Disables DM notifications for dormant help post. + * Enables/Disables DM notifications for dormant help posts. */ - PRIVATE_DORMANT_NOTIFICATIONS("Send notifications about dormant help post via DM", "true", new BooleanPreference()); + PRIVATE_DORMANT_NOTIFICATIONS("Send notifications about dormant help post via DM", "true", new BooleanPreference()), + + /** + * Enables/Disables DM notifications for closed help posts. + */ + PRIVATE_CLOSE_NOTIFICATIONS("Send notifications about help posts closed by other users via DM", "true", new BooleanPreference()); private final String name; private final String defaultState; From 4b43998dee446320f6f2bef483380f92a990d40d Mon Sep 17 00:00:00 2001 From: danthe1st Date: Tue, 1 Aug 2023 22:07:14 +0200 Subject: [PATCH 033/351] disable smart queue, update dependencies --- build.gradle.kts | 4 ++-- src/main/java/net/javadiscord/javabot/SpringConfig.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index e807fdffc..32efd9f7e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,8 +30,8 @@ dependencies { compileOnly("org.jetbrains:annotations:23.0.0") // DIH4JDA (Command Framework) & JDA - implementation("xyz.dynxsty:dih4jda:1.6.2") - implementation("net.dv8tion:JDA:5.0.0-beta.2") { + implementation("com.github.DynxstyGIT:DIH4JDA:120a15ad2e") + implementation("net.dv8tion:JDA:5.0.0-beta.12") { exclude(module = "opus-java") } diff --git a/src/main/java/net/javadiscord/javabot/SpringConfig.java b/src/main/java/net/javadiscord/javabot/SpringConfig.java index f9631b62f..7faf073af 100644 --- a/src/main/java/net/javadiscord/javabot/SpringConfig.java +++ b/src/main/java/net/javadiscord/javabot/SpringConfig.java @@ -14,7 +14,6 @@ import xyz.dynxsty.dih4jda.DIH4JDA; import xyz.dynxsty.dih4jda.DIH4JDABuilder; -import xyz.dynxsty.dih4jda.DIH4JDALogger; import xyz.dynxsty.dih4jda.exceptions.DIH4JDAException; import xyz.dynxsty.dih4jda.interactions.commands.application.RegistrationType; @@ -93,7 +92,8 @@ public JDA jda(BotConfig botConfig, ApplicationContext ctx) throws LoginExceptio public DIH4JDA initializeDIH4JDA(JDA jda) throws DIH4JDAException { DIH4JDA.setDefaultRegistrationType(RegistrationType.GLOBAL); return DIH4JDABuilder.setJDA(jda) - .disableLogging(DIH4JDALogger.Type.SMART_QUEUE_IGNORED) + .setGlobalSmartQueue(false) + .setGuildSmartQueue(false) .disableAutomaticCommandRegistration() .build(); } From 9b95da7988ed9636a18285e2c9675f6bb99fdc9a Mon Sep 17 00:00:00 2001 From: danthe1st Date: Fri, 4 Aug 2023 15:51:34 +0200 Subject: [PATCH 034/351] fix deprecated addActionRows --- .../java/net/javadiscord/javabot/data/h2db/DbHelper.java | 3 --- .../data/h2db/commands/QuickMigrateSubcommand.java | 2 +- .../javabot/systems/moderation/report/ReportManager.java | 4 ++-- .../commands/questions_queue/AddQuestionSubcommand.java | 2 +- .../staff_commands/embeds/AddEmbedFieldSubcommand.java | 2 +- .../staff_commands/embeds/CreateEmbedSubcommand.java | 2 +- .../staff_commands/embeds/EditEmbedSubcommand.java | 8 ++++---- .../self_roles/SelfRoleInteractionManager.java | 4 ++-- .../systems/staff_commands/tags/CustomTagManager.java | 2 -- .../tags/commands/CreateCustomTagSubcommand.java | 2 +- .../tags/commands/EditCustomTagSubcommand.java | 2 +- 11 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/javadiscord/javabot/data/h2db/DbHelper.java b/src/main/java/net/javadiscord/javabot/data/h2db/DbHelper.java index f1119db95..85fd667b5 100644 --- a/src/main/java/net/javadiscord/javabot/data/h2db/DbHelper.java +++ b/src/main/java/net/javadiscord/javabot/data/h2db/DbHelper.java @@ -22,7 +22,6 @@ import java.sql.Statement; import java.util.Arrays; import java.util.List; -import java.util.concurrent.ExecutorService; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -38,8 +37,6 @@ public class DbHelper { @Getter private final DataSource dataSource; - private final ExecutorService asyncPool; - private final BotConfig botConfig; /** * Initializes the data source that'll be used throughout the bot to access diff --git a/src/main/java/net/javadiscord/javabot/data/h2db/commands/QuickMigrateSubcommand.java b/src/main/java/net/javadiscord/javabot/data/h2db/commands/QuickMigrateSubcommand.java index cae4471c9..3022e6af3 100644 --- a/src/main/java/net/javadiscord/javabot/data/h2db/commands/QuickMigrateSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/data/h2db/commands/QuickMigrateSubcommand.java @@ -118,7 +118,7 @@ CREATE TABLE my_table ( .setRequired(true) .build(); return Modal.create("quick-migrate", "Quick Migrate") - .addActionRows(ActionRow.of(sqlInput), ActionRow.of(confirmInput)) + .addComponents(ActionRow.of(sqlInput), ActionRow.of(confirmInput)) .build(); } } diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java b/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java index 7f226fe77..7c86c5931 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java @@ -88,7 +88,7 @@ protected Modal buildUserReportModal(@NotNull UserContextInteractionEvent event) .build(); String title = "Report " + event.getTarget().getAsTag(); return Modal.create(ComponentIdBuilder.build("report", "user", event.getTarget().getId()), title.substring(0, Math.min(title.length(), Modal.MAX_TITLE_LENGTH))) - .addActionRows(ActionRow.of(messageInput)) + .addActionRow(messageInput) .build(); } @@ -109,7 +109,7 @@ protected Modal buildMessageReportModal(@NotNull MessageContextInteractionEvent .setMaxLength(MessageEmbed.VALUE_MAX_LENGTH) .build(); return Modal.create(ComponentIdBuilder.build("report", "message", event.getTarget().getId()), title.substring(0, Math.min(title.length(), Modal.MAX_TITLE_LENGTH))) - .addActionRows(ActionRow.of(messageInput)) + .addComponents(ActionRow.of(messageInput)) .build(); } diff --git a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/questions_queue/AddQuestionSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/questions_queue/AddQuestionSubcommand.java index 5b05eebe2..f47a31827 100644 --- a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/questions_queue/AddQuestionSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/questions_queue/AddQuestionSubcommand.java @@ -56,7 +56,7 @@ protected InteractionCallbackAction handleCommand(@NotNull SlashCommandIntera .build(); return Modal.create("qotw-add-question", "Create QOTW Question") - .addActionRows(ActionRow.of(questionField), ActionRow.of(priorityField)) + .addComponents(ActionRow.of(questionField), ActionRow.of(priorityField)) .build(); } diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/embeds/AddEmbedFieldSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/embeds/AddEmbedFieldSubcommand.java index 143cb9a13..d2186339a 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/embeds/AddEmbedFieldSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/embeds/AddEmbedFieldSubcommand.java @@ -106,7 +106,7 @@ public void handleModal(@NotNull ModalInteractionEvent event, @NotNull List(); } - private final DataSource dataSource; private final CustomTagRepository customTagRepository; /** diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/CreateCustomTagSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/CreateCustomTagSubcommand.java index 2fd67b512..2386d8848 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/CreateCustomTagSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/CreateCustomTagSubcommand.java @@ -80,7 +80,7 @@ public InteractionCallbackAction handleCustomTagsSubcommand(@NotNull Slash .setRequired(true) .build(); return Modal.create("tag-create", "Create Custom Tag") - .addActionRows(ActionRow.of(nameField), ActionRow.of(responseField), ActionRow.of(replyField), ActionRow.of(embedField)) + .addComponents(ActionRow.of(nameField), ActionRow.of(responseField), ActionRow.of(replyField), ActionRow.of(embedField)) .build(); } diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/EditCustomTagSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/EditCustomTagSubcommand.java index 19ea1308b..558f903ba 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/EditCustomTagSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/EditCustomTagSubcommand.java @@ -107,7 +107,7 @@ public InteractionCallbackAction handleCustomTagsSubcommand(@NotNull SlashCom .build(); return Modal.create(ComponentIdBuilder.build("tag-edit", tag.getName()), String.format("Edit \"%s\"", tag.getName().length() > 90 ? tag.getName().substring(0, 87) + "..." : tag.getName())) - .addActionRows(ActionRow.of(responseField), ActionRow.of(replyField), ActionRow.of(embedField)) + .addComponents(ActionRow.of(responseField), ActionRow.of(replyField), ActionRow.of(embedField)) .build(); } From 3c7db6a45d88dbdda62ba9c5596daa5b09eebdc8 Mon Sep 17 00:00:00 2001 From: 0x3C50 <99053360+0x3C50@users.noreply.github.com> Date: Sun, 18 Jun 2023 16:59:11 +0200 Subject: [PATCH 035/351] Strip "#0000" suffix from user tags using the new username system --- .../commands/MessageCacheInfoSubcommand.java | 3 +- .../data/h2db/message_cache/MessageCache.java | 31 ++++++++++--------- .../javabot/systems/help/HelpManager.java | 11 ++++--- .../help/commands/HelpAccountSubcommand.java | 3 +- .../help/commands/HelpPingSubcommand.java | 3 +- .../systems/moderation/ModerationService.java | 13 ++++---- .../systems/moderation/PruneCommand.java | 3 +- .../systems/moderation/PurgeCommand.java | 15 ++++----- .../moderation/report/ReportManager.java | 15 ++++----- .../server_lock/ServerLockManager.java | 21 +++++++++---- .../warn/DiscardAllWarnsSubcommand.java | 3 +- .../moderation/warn/WarnExportSubcommand.java | 3 +- .../moderation/warn/WarnsListCommand.java | 3 +- .../QOTWGuildNotificationService.java | 7 +++-- .../notification/QOTWNotificationService.java | 3 +- .../notification/UserNotificationService.java | 3 +- .../qotw_points/ChangePointsSubcommand.java | 3 +- .../ListQuestionsSubcommand.java | 3 +- .../qotw/submissions/SubmissionManager.java | 11 ++++--- .../staff_commands/RedeployCommand.java | 3 +- .../systems/staff_commands/SayCommand.java | 3 +- .../ChangeSelfRoleStatusSubcommand.java | 3 +- .../self_roles/CreateSelfRoleSubcommand.java | 3 +- .../self_roles/RemoveSelfRolesSubcommand.java | 3 +- .../SelfRoleInteractionManager.java | 9 +++--- .../AcceptSuggestionSubcommand.java | 3 +- .../DeclineSuggestionSubcommand.java | 3 +- .../OnHoldSuggestionSubcommand.java | 3 +- .../commands/CreateCustomTagSubcommand.java | 3 +- .../commands/DeleteCustomTagSubcommand.java | 3 +- .../commands/EditCustomTagSubcommand.java | 3 +- .../systems/starboard/StarboardManager.java | 3 +- .../systems/user_commands/AvatarCommand.java | 3 +- .../systems/user_commands/BotInfoCommand.java | 3 +- .../user_commands/ChangeMyMindCommand.java | 3 +- .../user_commands/IdCalculatorCommand.java | 3 +- .../systems/user_commands/PollCommand.java | 3 +- .../systems/user_commands/ProfileCommand.java | 3 +- .../ExperienceLeaderboardSubcommand.java | 3 +- .../QOTWLeaderboardSubcommand.java | 7 +++-- .../commands/PreferencesListSubcommand.java | 3 +- .../javabot/util/InteractionUtils.java | 8 ++--- .../javadiscord/javabot/util/UserUtils.java | 25 +++++++++++++++ 43 files changed, 170 insertions(+), 96 deletions(-) create mode 100644 src/main/java/net/javadiscord/javabot/util/UserUtils.java diff --git a/src/main/java/net/javadiscord/javabot/data/h2db/commands/MessageCacheInfoSubcommand.java b/src/main/java/net/javadiscord/javabot/data/h2db/commands/MessageCacheInfoSubcommand.java index 29a018063..e4eccca98 100644 --- a/src/main/java/net/javadiscord/javabot/data/h2db/commands/MessageCacheInfoSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/data/h2db/commands/MessageCacheInfoSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.data.h2db.commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.Permission; @@ -45,7 +46,7 @@ private MessageEmbed buildInfoEmbed(GuildConfig config, User author) { long messages = dbActions.count("SELECT count(*) FROM message_cache"); int maxMessages = config.getMessageCacheConfig().getMaxCachedMessages(); return new EmbedBuilder() - .setAuthor(author.getAsTag(), null, author.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(author), null, author.getEffectiveAvatarUrl()) .setTitle("Message Cache Info") .setColor(Responses.Type.DEFAULT.getColor()) .addField("Table Size", dbActions.getLogicalSize("message_cache") + " bytes", false) diff --git a/src/main/java/net/javadiscord/javabot/data/h2db/message_cache/MessageCache.java b/src/main/java/net/javadiscord/javabot/data/h2db/message_cache/MessageCache.java index 6cedd05fb..91c4d59e9 100644 --- a/src/main/java/net/javadiscord/javabot/data/h2db/message_cache/MessageCache.java +++ b/src/main/java/net/javadiscord/javabot/data/h2db/message_cache/MessageCache.java @@ -28,6 +28,7 @@ import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; +import net.javadiscord.javabot.util.UserUtils; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; @@ -142,7 +143,7 @@ public void sendDeletedMessageToLog(Guild guild, MessageChannel channel, CachedM private EmbedBuilder buildMessageCacheEmbed(MessageChannel channel, User author, CachedMessage before) { long epoch = IdCalculatorCommand.getTimestampFromId(before.getMessageId()) / 1000; return new EmbedBuilder() - .setAuthor(author.getAsTag(), null, author.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(author), null, author.getEffectiveAvatarUrl()) .addField("Author", author.getAsMention(), true) .addField("Channel", channel.getAsMention(), true) .addField("Created at", String.format("", epoch), true) @@ -200,14 +201,14 @@ private InputStream buildDeletedMessageFile(User author, CachedMessage message) DateTimeFormatter formatter = TimeUtils.STANDARD_FORMATTER.withZone(ZoneOffset.UTC); Instant instant = Instant.ofEpochMilli(IdCalculatorCommand.getTimestampFromId(message.getMessageId())); String in = String.format(""" - Author: %s - ID: %s - Created at: %s + Author: %s + ID: %s + Created at: %s - --- Message Content --- + --- Message Content --- - %s - """, author.getAsTag(), message.getMessageId(), formatter.format(instant), message.getMessageContent()); + %s + """, UserUtils.getUserTag(author), message.getMessageId(), formatter.format(instant), message.getMessageContent()); return new ByteArrayInputStream(in.getBytes(StandardCharsets.UTF_8)); } @@ -215,18 +216,18 @@ private InputStream buildEditedMessageFile(User author, CachedMessage before, Me DateTimeFormatter formatter = TimeUtils.STANDARD_FORMATTER.withZone(ZoneOffset.UTC); Instant instant = Instant.ofEpochMilli(IdCalculatorCommand.getTimestampFromId(before.getMessageId())); String in = String.format(""" - Author: %s - ID: %s - Created at: %s + Author: %s + ID: %s + Created at: %s - --- Message Content (before) --- + --- Message Content (before) --- - %s + %s - --- Message Content (after) --- + --- Message Content (after) --- - %s - """, author.getAsTag(), before.getMessageId(), formatter.format(instant), before.getMessageContent(), after.getContentRaw()); + %s + """, UserUtils.getUserTag(author), before.getMessageId(), formatter.format(instant), before.getMessageContent(), after.getContentRaw()); return new ByteArrayInputStream(in.getBytes(StandardCharsets.UTF_8)); } } diff --git a/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java b/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java index 0a2e1b120..8bc8baf69 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/HelpManager.java @@ -26,6 +26,7 @@ import net.javadiscord.javabot.util.ExceptionLogger; import net.javadiscord.javabot.util.MessageActionUtils; import net.javadiscord.javabot.util.Responses; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import xyz.dynxsty.dih4jda.util.ComponentIdBuilder; @@ -146,11 +147,11 @@ public void thankHelper(@NotNull ButtonInteractionEvent event, ThreadChannel pos } catch (SQLException e) { ExceptionLogger.capture(e, getClass().getSimpleName()); botConfig.get(event.getGuild()).getModerationConfig().getLogChannel().sendMessageFormat( - "Could not record user %s thanking %s for help in post %s: %s", - postThread.getOwner().getUser().getAsTag(), - helper.getAsTag(), - postThread.getAsMention(), - e.getMessage() + "Could not record user %s thanking %s for help in post %s: %s", + UserUtils.getUserTag(postThread.getOwner().getUser()), + UserUtils.getUserTag(helper), + postThread.getAsMention(), + e.getMessage() ).queue(); } }); diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java index 92114955c..f2a54cb9c 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpAccountSubcommand.java @@ -16,6 +16,7 @@ import net.javadiscord.javabot.util.Pair; import net.javadiscord.javabot.util.Responses; import net.javadiscord.javabot.util.StringUtils; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import org.springframework.dao.DataAccessException; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; @@ -66,7 +67,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { private @NotNull MessageEmbed buildHelpAccountEmbed(HelpAccount account, @NotNull User user, Guild guild, long totalThanks, long weekThanks) { return new EmbedBuilder() - .setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(user), null, user.getEffectiveAvatarUrl()) .setTitle("Help Account") .setThumbnail(user.getEffectiveAvatarUrl()) .setDescription("Here are some statistics about how you've helped others here.") diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java index 0e858364b..1bf1d41d4 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java @@ -16,6 +16,7 @@ import net.javadiscord.javabot.data.config.guild.HelpConfig; import net.javadiscord.javabot.util.Pair; import net.javadiscord.javabot.util.Responses; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; @@ -89,7 +90,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { private @NotNull MessageEmbed buildAuthorEmbed(@NotNull User author) { return new EmbedBuilder() - .setTitle("Requested by " + author.getAsTag()) + .setTitle("Requested by " + UserUtils.getUserTag(author)) .build(); } diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/ModerationService.java b/src/main/java/net/javadiscord/javabot/systems/moderation/ModerationService.java index 0ce9ca757..6c1bd5d61 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/ModerationService.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/ModerationService.java @@ -14,6 +14,7 @@ import net.javadiscord.javabot.systems.notification.NotificationService; import net.javadiscord.javabot.util.ExceptionLogger; import net.javadiscord.javabot.util.Responses; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import org.springframework.dao.DataAccessException; @@ -291,12 +292,12 @@ public void kick(User user, String reason, Member kickedBy, MessageChannel chann private @NotNull EmbedBuilder buildModerationEmbed(@NotNull User user, @NotNull Member moderator, String reason) { return new EmbedBuilder() - .setAuthor(moderator.getUser().getAsTag(), null, moderator.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(moderator.getUser()), null, moderator.getEffectiveAvatarUrl()) .addField("Member", user.getAsMention(), true) .addField("Moderator", moderator.getAsMention(), true) .addField("Reason", reason, true) .setTimestamp(Instant.now()) - .setFooter(user.getAsTag(), user.getEffectiveAvatarUrl()); + .setFooter(UserUtils.getUserTag(user), user.getEffectiveAvatarUrl()); } private @NotNull MessageEmbed buildBanEmbed(User user, Member bannedBy, String reason) { @@ -315,7 +316,7 @@ public void kick(User user, String reason, Member kickedBy, MessageChannel chann private @NotNull MessageEmbed buildUnbanEmbed(long userId, String reason, @NotNull Member unbannedBy) { return new EmbedBuilder() - .setAuthor(unbannedBy.getUser().getAsTag(), null, unbannedBy.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(unbannedBy.getUser()), null, unbannedBy.getEffectiveAvatarUrl()) .setTitle("Ban Revoked") .setColor(Responses.Type.ERROR.getColor()) .addField("Moderator", unbannedBy.getAsMention(), true) @@ -335,18 +336,18 @@ public void kick(User user, String reason, Member kickedBy, MessageChannel chann private @NotNull MessageEmbed buildClearWarnsEmbed(@NotNull User user, @NotNull User clearedBy) { return new EmbedBuilder() - .setAuthor(clearedBy.getAsTag(), null, clearedBy.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(clearedBy), null, clearedBy.getEffectiveAvatarUrl()) .setTitle("Warns Cleared") .setColor(Responses.Type.WARN.getColor()) .setDescription("All warns have been cleared from " + user.getAsMention() + "'s record.") .setTimestamp(Instant.now()) - .setFooter(user.getAsTag(), user.getEffectiveAvatarUrl()) + .setFooter(UserUtils.getUserTag(user), user.getEffectiveAvatarUrl()) .build(); } private @NotNull MessageEmbed buildClearWarnsByIdEmbed(@NotNull Warn w, @NotNull User clearedBy) { return new EmbedBuilder() - .setAuthor(clearedBy.getAsTag(), null, clearedBy.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(clearedBy), null, clearedBy.getEffectiveAvatarUrl()) .setTitle("Warn Cleared") .setColor(Responses.Type.WARN.getColor()) .setDescription(String.format(""" diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/PruneCommand.java b/src/main/java/net/javadiscord/javabot/systems/moderation/PruneCommand.java index 7bb84e129..3fecab317 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/PruneCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/PruneCommand.java @@ -10,6 +10,7 @@ import net.javadiscord.javabot.data.config.guild.ModerationConfig; import net.javadiscord.javabot.util.Checks; import net.javadiscord.javabot.util.Responses; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import java.time.LocalDateTime; @@ -77,7 +78,7 @@ protected ReplyCallbackAction handleModerationCommand(@NotNull SlashCommandInter (before == null || member.getTimeJoined().isBefore(before)) && (after == null || member.getTimeJoined().isAfter(after)); if (shouldRemove) { - config.getLogChannel().sendMessage("Removing " + member.getUser().getAsTag() + " as part of prune.").queue(); + config.getLogChannel().sendMessage("Removing " + UserUtils.getUserTag(member.getUser()) + " as part of prune.").queue(); member.ban(delDays, TimeUnit.DAYS).reason(reason).queue(); } }); diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/PurgeCommand.java b/src/main/java/net/javadiscord/javabot/systems/moderation/PurgeCommand.java index 378de8b3d..defa17b6f 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/PurgeCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/PurgeCommand.java @@ -16,6 +16,7 @@ import net.javadiscord.javabot.util.ExceptionLogger; import net.javadiscord.javabot.util.Responses; import net.javadiscord.javabot.util.TimeUtils; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import javax.annotation.CheckReturnValue; @@ -74,7 +75,7 @@ protected ReplyCallbackAction handleModerationCommand(@NotNull SlashCommandInter StringBuilder sb = new StringBuilder(); sb.append(amount > 1 ? "Up to " + amount + " messages " : "1 message "); if (user != null) { - sb.append("by the user ").append(user.getAsTag()).append(' '); + sb.append("by the user ").append(UserUtils.getUserTag(user)).append(' '); } sb.append("will be removed").append(archive ? " and placed in an archive." : '.'); return Responses.info(event, "Purge Started", sb.toString()); @@ -198,12 +199,12 @@ private int removeMessages(List messages, @Nullable User user, @Nullabl */ private void archiveMessage(PrintWriter writer, Message message) { writer.printf( - "%s : Removing message %s by %s which was sent at %s\n--- Text ---\n%s\n--- End Text ---\n\n", - OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), - message.getId(), - message.getAuthor().getAsTag(), - message.getTimeCreated().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), - message.getContentRaw() + "%s : Removing message %s by %s which was sent at %s\n--- Text ---\n%s\n--- End Text ---\n\n", + OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), + message.getId(), + UserUtils.getUserTag(message.getAuthor()), + message.getTimeCreated().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME), + message.getContentRaw() ); } } \ No newline at end of file diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java b/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java index 7f226fe77..7f86e8fe3 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.moderation.report; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.util.ComponentIdBuilder; import xyz.dynxsty.dih4jda.interactions.components.ButtonHandler; import xyz.dynxsty.dih4jda.interactions.components.ModalHandler; @@ -56,7 +57,7 @@ public void handleButton(@NotNull ButtonInteractionEvent event, Button button) { return; } Responses.info(event.getHook(), "Report resolved", "Successfully resolved this report!").queue(); - event.getMessage().editMessageComponents(ActionRow.of(Button.secondary("report-resolved", "Resolved by " + event.getUser().getAsTag()).asDisabled())).queue(); + event.getMessage().editMessageComponents(ActionRow.of(Button.secondary("report-resolved", "Resolved by " + UserUtils.getUserTag(event.getUser())).asDisabled())).queue(); thread.sendMessage("This thread was resolved by " + event.getUser().getAsMention()).queue( success -> thread.getManager() .setName(String.format("[Resolved] %s", thread.getName())) @@ -86,7 +87,7 @@ protected Modal buildUserReportModal(@NotNull UserContextInteractionEvent event) TextInput messageInput = TextInput.create("reason", "Report Description", TextInputStyle.PARAGRAPH) .setMaxLength(MessageEmbed.VALUE_MAX_LENGTH) .build(); - String title = "Report " + event.getTarget().getAsTag(); + String title = "Report " + UserUtils.getUserTag(event.getTarget()); return Modal.create(ComponentIdBuilder.build("report", "user", event.getTarget().getId()), title.substring(0, Math.min(title.length(), Modal.MAX_TITLE_LENGTH))) .addActionRows(ActionRow.of(messageInput)) .build(); @@ -103,7 +104,7 @@ protected Modal buildMessageReportModal(@NotNull MessageContextInteractionEvent String title = "Report message"; Member targetMember = event.getTarget().getMember(); if (targetMember != null) { - title += " from " + targetMember.getUser().getAsTag(); + title += " from " + UserUtils.getUserTag(targetMember.getUser()); } TextInput messageInput = TextInput.create("reason", "Report Description", TextInputStyle.PARAGRAPH) .setMaxLength(MessageEmbed.VALUE_MAX_LENGTH) @@ -136,7 +137,7 @@ protected WebhookMessageCreateAction handleUserReport(InteractionHook h } reportChannel.sendMessageEmbeds(embed.build()) .queue(m -> this.createReportThread(m, target.getIdLong(), config.getModerationConfig())); - embed.setDescription("Successfully reported " + "`" + target.getAsTag() + "`!\nYour report has been send to our Moderators"); + embed.setDescription("Successfully reported " + "`" + UserUtils.getUserTag(target) + "`!\nYour report has been send to our Moderators"); hook.sendMessageEmbeds(embed.build()).queue(); }, failure -> { Responses.error(hook, "The user to report seems not to exist any more.").queue(); @@ -162,7 +163,7 @@ private void handleMessageReport(ModalInteractionEvent event, String messageId) WebhookUtil.mirrorMessageToWebhook(wh, target, target.getContentRaw(), thread.getIdLong(), null, null); }); })); - embed.setDescription("Successfully reported " + "`" + target.getAuthor().getAsTag() + "`!\nYour report has been send to our Moderators"); + embed.setDescription("Successfully reported " + "`" + UserUtils.getUserTag(target.getAuthor()) + "`!\nYour report has been send to our Moderators"); event.getHook().sendMessageEmbeds(embed.build()).queue(); }, failure -> { Responses.error(event.getHook(), "The author of the message to report seems not to exist any more.").queue(); @@ -201,7 +202,7 @@ private void createReportThread(Message message, long targetId, ModerationConfig private EmbedBuilder buildReportEmbed(User reported, User reportedBy, String reason, Channel channel) { return new EmbedBuilder() - .setAuthor(reported.getAsTag(), null, reported.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(reported), null, reported.getEffectiveAvatarUrl()) .setColor(Responses.Type.DEFAULT.getColor()) .addField("Member", reported.getAsMention(), true) .addField("Reported by", reportedBy.getAsMention(), true) @@ -209,7 +210,7 @@ private EmbedBuilder buildReportEmbed(User reported, User reportedBy, String rea .addField("Reported on", String.format("", Instant.now().getEpochSecond()), false) .addField("ID", String.format("``` %s ```", reported.getId()), true) .addField("Reason", String.format("``` %s ```", reason), false) - .setFooter(reportedBy.getAsTag(), reportedBy.getEffectiveAvatarUrl()) + .setFooter(UserUtils.getUserTag(reportedBy), reportedBy.getEffectiveAvatarUrl()) .setTimestamp(Instant.now()); } } diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/server_lock/ServerLockManager.java b/src/main/java/net/javadiscord/javabot/systems/moderation/server_lock/ServerLockManager.java index d92b31f59..9fe8f4b28 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/server_lock/ServerLockManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/server_lock/ServerLockManager.java @@ -17,6 +17,7 @@ import net.javadiscord.javabot.data.config.guild.ServerLockConfig; import net.javadiscord.javabot.util.Responses; import net.javadiscord.javabot.util.TimeUtils; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -202,7 +203,11 @@ private void rejectUserDuringRaid(@NotNull GuildMemberJoinEvent event) { c.sendMessage(Constants.INVITE_URL).setEmbeds(buildServerLockEmbed(event.getGuild())).queue(msg -> event.getMember().kick().queue())); String diff = new TimeUtils().formatDurationToNow(event.getMember().getTimeCreated()); - notificationService.withGuild(event.getGuild()).sendToModerationLog(c -> c.sendMessageFormat("**%s** (%s old) tried to join this server.", event.getMember().getUser().getAsTag(), diff)); + notificationService.withGuild(event.getGuild()).sendToModerationLog(c -> c.sendMessageFormat( + "**%s** (%s old) tried to join this server.", + UserUtils.getUserTag(event.getMember().getUser()), + diff + )); } /** @@ -219,7 +224,11 @@ public void lockServer(Guild guild, @NotNull Collection potentialRaiders c.sendMessage(Constants.INVITE_URL).setEmbeds(buildServerLockEmbed(guild)).queue(msg -> { member.kick().queue( success -> {}, - error -> notificationService.withGuild(guild).sendToModerationLog(m -> m.sendMessageFormat("Could not kick member %s%n> `%s`", member.getUser().getAsTag(), error.getMessage()))); + error -> notificationService.withGuild(guild).sendToModerationLog(m -> m.sendMessageFormat( + "Could not kick member %s%n> `%s`", + UserUtils.getUserTag(member.getUser()), + error.getMessage() + ))); }); }); } @@ -227,10 +236,10 @@ public void lockServer(Guild guild, @NotNull Collection potentialRaiders String membersString = potentialRaiders.stream() .sorted(Comparator.comparing(Member::getTimeJoined).reversed()) .map(m -> String.format( - "- **%s** joined at `%s`, account is `%s` old.", - m.getUser().getAsTag(), - m.getTimeJoined().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSSS")), - TimeUtils.formatDuration(Duration.between(m.getTimeCreated(), OffsetDateTime.now())) + "- **%s** joined at `%s`, account is `%s` old.", + UserUtils.getUserTag(m.getUser()), + m.getTimeJoined().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSSS")), + TimeUtils.formatDuration(Duration.between(m.getTimeCreated(), OffsetDateTime.now())) )) .collect(Collectors.joining("\n")); diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/warn/DiscardAllWarnsSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/moderation/warn/DiscardAllWarnsSubcommand.java index 1642221c7..9ddace733 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/warn/DiscardAllWarnsSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/warn/DiscardAllWarnsSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.moderation.warn; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -55,7 +56,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { } User target = userMapping.getAsUser(); new ModerationService(notificationService, botConfig, event.getInteraction(), warnRepository, asyncPool).discardAllWarns(target, event.getMember()); - Responses.success(event, "Warns Discarded", "Successfully discarded all warns from **%s**.", target.getAsTag()).queue(); + Responses.success(event, "Warns Discarded", "Successfully discarded all warns from **%s**.", UserUtils.getUserTag(target)).queue(); } } diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/warn/WarnExportSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/moderation/warn/WarnExportSubcommand.java index 8aa39ab15..6682bdf46 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/warn/WarnExportSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/warn/WarnExportSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.moderation.warn; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.User; @@ -76,7 +77,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { PipedInputStream pis=new PipedInputStream(); try(PrintWriter pw=new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(new PipedOutputStream(pis)), StandardCharsets.UTF_8))){ event.replyEmbeds(new EmbedBuilder() - .setAuthor(target.getAsTag(), null, target.getAvatarUrl()) + .setAuthor(UserUtils.getUserTag(target), null, target.getAvatarUrl()) .setDescription("Export containing all warns of "+target.getAsMention()) .addField("Total number of warns", String.valueOf(warns.stream().count()), false) .addField("Total number of non-discarded warns (includes expired warns)", String.valueOf(warns.stream().filter(w->!w.isDiscarded()).count()), false) diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/warn/WarnsListCommand.java b/src/main/java/net/javadiscord/javabot/systems/moderation/warn/WarnsListCommand.java index 9bb6ad630..09c3ca7c8 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/warn/WarnsListCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/warn/WarnsListCommand.java @@ -8,6 +8,7 @@ import javax.annotation.Nonnull; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import org.springframework.dao.DataAccessException; @@ -60,7 +61,7 @@ public WarnsListCommand(BotConfig botConfig, ExecutorService asyncPool, WarnRepo */ protected static @NotNull MessageEmbed buildWarnsEmbed(@Nonnull List warns, @Nonnull User user) { EmbedBuilder builder = new EmbedBuilder() - .setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(user), null, user.getEffectiveAvatarUrl()) .setTitle("Recent Warns") .setDescription(String.format("%s has `%s` active warns with a total of `%s` severity.\n", user.getAsMention(), warns.size(), warns.stream().mapToInt(Warn::getSeverityWeight).sum())) diff --git a/src/main/java/net/javadiscord/javabot/systems/notification/QOTWGuildNotificationService.java b/src/main/java/net/javadiscord/javabot/systems/notification/QOTWGuildNotificationService.java index a248b3873..ef0869f05 100644 --- a/src/main/java/net/javadiscord/javabot/systems/notification/QOTWGuildNotificationService.java +++ b/src/main/java/net/javadiscord/javabot/systems/notification/QOTWGuildNotificationService.java @@ -11,6 +11,7 @@ import net.javadiscord.javabot.systems.qotw.model.QOTWSubmission; import net.javadiscord.javabot.systems.qotw.submissions.SubmissionStatus; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import java.time.Instant; @@ -37,14 +38,14 @@ public class QOTWGuildNotificationService { public void sendSubmissionActionNotification(User reviewedBy, @NotNull QOTWSubmission submission, SubmissionStatus status) { submission.retrieveAuthor(author -> { notificationService.withGuild(guild).sendToModerationLog(c -> c.sendMessageEmbeds(buildSubmissionActionEmbed(author, submission.getThread(), reviewedBy, status))); - log.info("{} {} {}'s QOTW Submission", reviewedBy.getAsTag(), status.getVerb(), author.getAsTag()); + log.info("{} {} {}'s QOTW Submission", UserUtils.getUserTag(reviewedBy), status.getVerb(), UserUtils.getUserTag(author)); }); } private @NotNull MessageEmbed buildSubmissionActionEmbed(@NotNull User author, ThreadChannel thread, @NotNull User reviewedBy, @NotNull SubmissionStatus status) { EmbedBuilder builder = new EmbedBuilder() - .setAuthor(reviewedBy.getAsTag(), null, reviewedBy.getEffectiveAvatarUrl()) - .setTitle(String.format("%s %s %s's QOTW Submission", reviewedBy.getAsTag(), status.getVerb(), author.getAsTag())) + .setAuthor(UserUtils.getUserTag(reviewedBy), null, reviewedBy.getEffectiveAvatarUrl()) + .setTitle(String.format("%s %s %s's QOTW Submission", UserUtils.getUserTag(reviewedBy), status.getVerb(), UserUtils.getUserTag(author))) .setTimestamp(Instant.now()); if (thread != null) { builder.addField("Thread", thread.getAsMention(), true); diff --git a/src/main/java/net/javadiscord/javabot/systems/notification/QOTWNotificationService.java b/src/main/java/net/javadiscord/javabot/systems/notification/QOTWNotificationService.java index dfc880c82..48d8512a9 100644 --- a/src/main/java/net/javadiscord/javabot/systems/notification/QOTWNotificationService.java +++ b/src/main/java/net/javadiscord/javabot/systems/notification/QOTWNotificationService.java @@ -10,6 +10,7 @@ import net.javadiscord.javabot.systems.qotw.model.QOTWAccount; import net.javadiscord.javabot.systems.qotw.submissions.SubmissionStatus; import net.javadiscord.javabot.util.Responses; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import org.springframework.dao.DataAccessException; @@ -70,7 +71,7 @@ public void sendSubmissionDeclinedEmbed(SubmissionStatus status) { private @NotNull EmbedBuilder buildQOTWNotificationEmbed() { return new EmbedBuilder() - .setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(user), null, user.getEffectiveAvatarUrl()) .setTitle("QOTW Notification") .setTimestamp(Instant.now()); } diff --git a/src/main/java/net/javadiscord/javabot/systems/notification/UserNotificationService.java b/src/main/java/net/javadiscord/javabot/systems/notification/UserNotificationService.java index 6e727e8bc..58de848eb 100644 --- a/src/main/java/net/javadiscord/javabot/systems/notification/UserNotificationService.java +++ b/src/main/java/net/javadiscord/javabot/systems/notification/UserNotificationService.java @@ -11,6 +11,7 @@ import net.dv8tion.jda.api.requests.restaction.MessageCreateAction; import net.javadiscord.javabot.data.config.guild.ModerationConfig; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import java.util.function.Function; @@ -34,7 +35,7 @@ public void sendDirectMessage(@NotNull Function {}, error -> { - log.error("Could not open PrivateChannel with user " + user.getAsTag(), error); + log.error("Could not open PrivateChannel with user " + UserUtils.getUserTag(user), error); if(config != null) { TextChannel container = config.getNotificationThreadChannel(); if(container != null) { diff --git a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/ChangePointsSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/ChangePointsSubcommand.java index 84596652a..31da24a08 100644 --- a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/ChangePointsSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/qotw_points/ChangePointsSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.qotw.commands.qotw_points; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Member; @@ -90,7 +91,7 @@ private void sendNotifications(SlashCommandInteractionEvent event, Member member */ protected @NotNull EmbedBuilder createIncrementEmbedBuilder(User user, long points) { return new EmbedBuilder() - .setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(user), null, user.getEffectiveAvatarUrl()) .setTitle("QOTW Account changed") .setColor(Responses.Type.SUCCESS.getColor()) .addField("Total QOTW-Points", "```" + points + "```", true) diff --git a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/questions_queue/ListQuestionsSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/questions_queue/ListQuestionsSubcommand.java index 562f0fcf0..2c91a2143 100644 --- a/src/main/java/net/javadiscord/javabot/systems/qotw/commands/questions_queue/ListQuestionsSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/qotw/commands/questions_queue/ListQuestionsSubcommand.java @@ -10,6 +10,7 @@ import net.javadiscord.javabot.systems.qotw.dao.QuestionQueueRepository; import net.javadiscord.javabot.systems.qotw.model.QOTWQuestion; import net.javadiscord.javabot.util.Responses; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import org.springframework.dao.DataAccessException; import org.springframework.transaction.annotation.Transactional; @@ -52,7 +53,7 @@ protected InteractionCallbackAction handleCommand(@NotNull SlashCommandIntera } List questions = questionQueueRepository.getQuestions(guildId, page, 10); EmbedBuilder embedBuilder = new EmbedBuilder() - .setAuthor(event.getUser().getAsTag(), null, event.getUser().getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(event.getUser()), null, event.getUser().getEffectiveAvatarUrl()) .setTitle("QOTW Questions Queue") .setColor(Responses.Type.DEFAULT.getColor()); if (questions.isEmpty()) { diff --git a/src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionManager.java b/src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionManager.java index c8287252c..a123b9623 100644 --- a/src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/qotw/submissions/SubmissionManager.java @@ -24,6 +24,7 @@ import net.javadiscord.javabot.systems.qotw.model.QOTWSubmission; import net.javadiscord.javabot.util.ExceptionLogger; import net.javadiscord.javabot.util.Responses; +import net.javadiscord.javabot.util.UserUtils; import net.javadiscord.javabot.util.WebhookUtil; import org.jetbrains.annotations.NotNull; import org.springframework.transaction.annotation.Transactional; @@ -88,9 +89,9 @@ public WebhookMessageCreateAction handleSubmission(@NotNull ButtonInteraction .queue(); } }); - }, e -> log.error("Could not create submission thread for member {}. ", member.getUser().getAsTag(), e) + }, e -> log.error("Could not create submission thread for member {}. ", UserUtils.getUserTag(member.getUser()), e) ); - log.info("Opened new Submission Thread for User {}", member.getUser().getAsTag()); + log.info("Opened new Submission Thread for User {}", UserUtils.getUserTag(member.getUser())); return Responses.success(event.getHook(), "Submission Thread created", "Successfully created a new private Thread for your submission."); } @@ -153,7 +154,7 @@ public void handleSelectReview(StringSelectInteractionEvent event, String thread reviewChannel.asThreadChannel().getManager().setArchived(true).queue(); } } - event.getHook().editOriginalComponents(ActionRow.of(Button.secondary("dummy", "%s by %s".formatted(status.getVerb(), event.getUser().getAsTag())).asDisabled())).queue(); + event.getHook().editOriginalComponents(ActionRow.of(Button.secondary("dummy", "%s by %s".formatted(status.getVerb(), UserUtils.getUserTag(event.getUser()))).asDisabled())).queue(); }); } @@ -245,7 +246,7 @@ private CompletableFuture> getMessagesByUser(@NotNull ThreadChanne private @NotNull MessageEmbed buildAuthorEmbed(@NotNull User user, boolean bestAnswer) { return new EmbedBuilder() - .setAuthor((bestAnswer ? "\u2B50 " : "") + "Submission from " + user.getAsTag(), null, user.getAvatarUrl()) + .setAuthor((bestAnswer ? "\u2B50 " : "") + "Submission from " + UserUtils.getUserTag(user), null, user.getAvatarUrl()) .setColor(bestAnswer ? Responses.Type.WARN.getColor() : Responses.Type.DEFAULT.getColor()) .build(); } @@ -253,7 +254,7 @@ private CompletableFuture> getMessagesByUser(@NotNull ThreadChanne private @NotNull MessageEmbed buildSubmissionThreadEmbed(@NotNull User createdBy, @NotNull QOTWQuestion question, @NotNull QOTWConfig config) { return new EmbedBuilder() .setColor(Responses.Type.DEFAULT.getColor()) - .setAuthor(createdBy.getAsTag(), null, createdBy.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(createdBy), null, createdBy.getEffectiveAvatarUrl()) .setTitle(String.format("Question of the Week #%s", question.getQuestionNumber())) .setDescription(String.format(""" %s diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/RedeployCommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/RedeployCommand.java index 4c0b6f1a1..bafb13e2d 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/RedeployCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/RedeployCommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.staff_commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -46,7 +47,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { Responses.replyAdminOnly(event, botConfig.get(event.getGuild())).queue(); return; } - log.warn("Redeploying... Requested by: " + event.getUser().getAsTag()); + log.warn("Redeploying... Requested by: " + UserUtils.getUserTag(event.getUser())); event.reply("**Redeploying...** This may take some time.").queue(); messageCache.synchronize(); System.exit(0); diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/SayCommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/SayCommand.java index 35a18343a..fce307c90 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/SayCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/SayCommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.staff_commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Message; @@ -48,7 +49,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { return; } String text = textMapping.getAsString(); - log.info("Posted \"{}\" in \"#{}\" as requested by \"{}\"", text, event.getChannel().getName(), event.getUser().getAsTag()); + log.info("Posted \"{}\" in \"#{}\" as requested by \"{}\"", text, event.getChannel().getName(), UserUtils.getUserTag(event.getUser())); event.deferReply(true).queue(); event.getChannel().sendMessage(text) .setAllowedMentions(Set.of(Message.MentionType.EMOJI, Message.MentionType.CHANNEL)) diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/ChangeSelfRoleStatusSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/ChangeSelfRoleStatusSubcommand.java index 60559a3d9..a51c3f4cb 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/ChangeSelfRoleStatusSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/ChangeSelfRoleStatusSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.staff_commands.self_roles; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Message; @@ -57,7 +58,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { private @NotNull MessageEmbed buildSelfRoleStatusEmbed(@NotNull User changedBy, @NotNull Message message, boolean disabled) { return new EmbedBuilder() - .setAuthor(changedBy.getAsTag(), message.getJumpUrl(), changedBy.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(changedBy), message.getJumpUrl(), changedBy.getEffectiveAvatarUrl()) .setTitle("Self Role " + (disabled ? "disabled" : "enabled")) .setColor(Responses.Type.DEFAULT.getColor()) .addField("Channel", message.getChannel().getAsMention(), true) diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/CreateSelfRoleSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/CreateSelfRoleSubcommand.java index 38889ca02..d05fd8446 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/CreateSelfRoleSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/CreateSelfRoleSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.staff_commands.self_roles; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.*; @@ -122,7 +123,7 @@ private String buildButtonId(@NotNull String type, @NotNull Role role, boolean p private @NotNull MessageEmbed buildSelfRoleCreateEmbed(@NotNull User createdBy, @NotNull Role role, @NotNull Channel channel, String jumpUrl, String type) { return new EmbedBuilder() - .setAuthor(createdBy.getAsTag(), jumpUrl, createdBy.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(createdBy), jumpUrl, createdBy.getEffectiveAvatarUrl()) .setTitle("Self Role created") .setColor(Responses.Type.DEFAULT.getColor()) .addField("Channel", channel.getAsMention(), true) diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/RemoveSelfRolesSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/RemoveSelfRolesSubcommand.java index 1c2741b2f..acd4e9b97 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/RemoveSelfRolesSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/RemoveSelfRolesSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.staff_commands.self_roles; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Message; @@ -49,7 +50,7 @@ public void execute(SlashCommandInteractionEvent event) { private @NotNull MessageEmbed buildSelfRoleDeletedEmbed(@NotNull User changedBy, @NotNull Message message) { return new EmbedBuilder() - .setAuthor(changedBy.getAsTag(), message.getJumpUrl(), changedBy.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(changedBy), message.getJumpUrl(), changedBy.getEffectiveAvatarUrl()) .setTitle("Self Roles Removed") .setColor(Responses.Type.DEFAULT.getColor()) .addField("Channel", message.getChannel().getAsMention(), true) diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/SelfRoleInteractionManager.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/SelfRoleInteractionManager.java index 96820313e..3dc4666c8 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/SelfRoleInteractionManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/self_roles/SelfRoleInteractionManager.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.staff_commands.self_roles; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.util.ComponentIdBuilder; import xyz.dynxsty.dih4jda.interactions.components.ButtonHandler; import xyz.dynxsty.dih4jda.interactions.components.ModalHandler; @@ -195,8 +196,8 @@ private WebhookMessageCreateAction sendStaffSubmission(@NotNull ModalIn member -> { User user = member.getUser(); MessageEmbed embed = new EmbedBuilder() - .setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl()) - .setTitle(String.format("%s applied for %s", user.getAsTag(), role.getName())) + .setAuthor(UserUtils.getUserTag(user), null, user.getEffectiveAvatarUrl()) + .setTitle(String.format("%s applied for %s", UserUtils.getUserTag(user), role.getName())) .setColor(Responses.Type.SUCCESS.getColor()) .addField("Real Name", nameOption.getAsString(), false) .addField("Age", ageOption.getAsString(), true) @@ -237,8 +238,8 @@ private WebhookMessageCreateAction sendStaffSubmission(@NotNull ModalIn member -> { User user = member.getUser(); EmbedBuilder embed = new EmbedBuilder() - .setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl()) - .setTitle(String.format("%s applied for %s", user.getAsTag(), config.getExpertRole().getName())) + .setAuthor(UserUtils.getUserTag(user), null, user.getEffectiveAvatarUrl()) + .setTitle(String.format("%s applied for %s", UserUtils.getUserTag(user), config.getExpertRole().getName())) .setColor(config.getExpertRole().getColor()) .addField("How much Java experience do you have?", experienceOption.getAsString(), false) .addField("Present us a fitting Java Project", projectInfoOption.getAsString(), false) diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/AcceptSuggestionSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/AcceptSuggestionSubcommand.java index 053dc230a..c2789b0cf 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/AcceptSuggestionSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/AcceptSuggestionSubcommand.java @@ -11,6 +11,7 @@ import net.dv8tion.jda.api.requests.restaction.WebhookMessageCreateAction; import net.javadiscord.javabot.data.config.GuildConfig; import net.javadiscord.javabot.util.Responses; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; /** @@ -46,7 +47,7 @@ protected WebhookMessageCreateAction handleSuggestionCommand(@NotNull S .setTitle("Suggestion Accepted") .setDescription(embed.getDescription()) .setTimestamp(embed.getTimestamp()) - .setFooter("Accepted by " + user.getAsTag()) + .setFooter("Accepted by " + UserUtils.getUserTag(user)) .build(); } } \ No newline at end of file diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/DeclineSuggestionSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/DeclineSuggestionSubcommand.java index 7d597140d..80bde59cd 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/DeclineSuggestionSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/DeclineSuggestionSubcommand.java @@ -12,6 +12,7 @@ import net.dv8tion.jda.api.requests.restaction.WebhookMessageCreateAction; import net.javadiscord.javabot.data.config.GuildConfig; import net.javadiscord.javabot.util.Responses; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; /** @@ -48,7 +49,7 @@ protected WebhookMessageCreateAction handleSuggestionCommand(@NotNull S .setTitle("Suggestion Declined") .setDescription(embed.getDescription()) .setTimestamp(embed.getTimestamp()) - .setFooter("Declined by " + user.getAsTag()); + .setFooter("Declined by " + UserUtils.getUserTag(user)); if (reason != null) builder.addField("Reason", reason, false); return builder.build(); } diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/OnHoldSuggestionSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/OnHoldSuggestionSubcommand.java index 9f8335b3c..c518bc414 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/OnHoldSuggestionSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/suggestions/OnHoldSuggestionSubcommand.java @@ -11,6 +11,7 @@ import net.dv8tion.jda.api.requests.restaction.WebhookMessageCreateAction; import net.javadiscord.javabot.data.config.GuildConfig; import net.javadiscord.javabot.util.Responses; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; /** @@ -47,7 +48,7 @@ protected WebhookMessageCreateAction handleSuggestionCommand(@NotNull S .setTitle("Suggestion On Hold") .setDescription(embed.getDescription()) .setTimestamp(embed.getTimestamp()) - .setFooter("Suggestion marked as On Hold by " + user.getAsTag()) + .setFooter("Suggestion marked as On Hold by " + UserUtils.getUserTag(user)) .build(); } } \ No newline at end of file diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/CreateCustomTagSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/CreateCustomTagSubcommand.java index 2fd67b512..f624dea2a 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/CreateCustomTagSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/CreateCustomTagSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.staff_commands.tags.commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.components.ModalHandler; import net.dv8tion.jda.api.EmbedBuilder; @@ -86,7 +87,7 @@ public InteractionCallbackAction handleCustomTagsSubcommand(@NotNull Slash private @NotNull MessageEmbed buildCreateCommandEmbed(@NotNull User createdBy, @NotNull CustomTag command) { return new EmbedBuilder() - .setAuthor(createdBy.getAsTag(), null, createdBy.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(createdBy), null, createdBy.getEffectiveAvatarUrl()) .setTitle("Custom Tag Created") .addField("Id", String.format("`%s`", command.getId()), true) .addField("Name", String.format("`%s`", command.getName()), true) diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/DeleteCustomTagSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/DeleteCustomTagSubcommand.java index 973dda0cd..2bab40472 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/DeleteCustomTagSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/DeleteCustomTagSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.staff_commands.tags.commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.AutoCompletable; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Member; @@ -77,7 +78,7 @@ public ReplyCallbackAction handleCustomTagsSubcommand(@NotNull SlashCommandInter private @NotNull MessageEmbed buildDeleteCommandEmbed(@NotNull Member deletedBy, @NotNull CustomTag command) { return new EmbedBuilder() - .setAuthor(deletedBy.getUser().getAsTag(), null, deletedBy.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(deletedBy.getUser()), null, deletedBy.getEffectiveAvatarUrl()) .setTitle("Custom Tag Deleted") .addField("Id", String.format("`%s`", command.getId()), true) .addField("Name", String.format("`%s`", command.getName()), true) diff --git a/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/EditCustomTagSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/EditCustomTagSubcommand.java index 19ea1308b..3094fafe6 100644 --- a/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/EditCustomTagSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/staff_commands/tags/commands/EditCustomTagSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.staff_commands.tags.commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.util.ComponentIdBuilder; import xyz.dynxsty.dih4jda.interactions.AutoCompletable; import xyz.dynxsty.dih4jda.interactions.components.ModalHandler; @@ -113,7 +114,7 @@ public InteractionCallbackAction handleCustomTagsSubcommand(@NotNull SlashCom private @NotNull MessageEmbed buildEditTagEmbed(@NotNull Member createdBy, @NotNull CustomTag command) { return new EmbedBuilder() - .setAuthor(createdBy.getUser().getAsTag(), null, createdBy.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(createdBy.getUser()), null, createdBy.getEffectiveAvatarUrl()) .setTitle("Custom Tag Edited") .addField("Id", String.format("`%s`", command.getId()), true) .addField("Name", String.format("`%s`", command.getName()), true) diff --git a/src/main/java/net/javadiscord/javabot/systems/starboard/StarboardManager.java b/src/main/java/net/javadiscord/javabot/systems/starboard/StarboardManager.java index 542e3f8a3..938d5d365 100644 --- a/src/main/java/net/javadiscord/javabot/systems/starboard/StarboardManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/starboard/StarboardManager.java @@ -22,6 +22,7 @@ import net.javadiscord.javabot.systems.starboard.model.StarboardEntry; import net.javadiscord.javabot.util.ExceptionLogger; import net.javadiscord.javabot.util.Responses; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import org.springframework.dao.DataAccessException; @@ -221,7 +222,7 @@ private boolean removeMessageFromStarboard(long messageId, MessageChannel channe private @NotNull MessageEmbed buildStarboardEmbed(@NotNull Message message) { User author = message.getAuthor(); return new EmbedBuilder() - .setAuthor(author.getAsTag(), message.getJumpUrl(), author.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(author), message.getJumpUrl(), author.getEffectiveAvatarUrl()) .setColor(Responses.Type.DEFAULT.getColor()) .setDescription(message.getContentRaw()) .setFooter("#" + message.getChannel().getName()) diff --git a/src/main/java/net/javadiscord/javabot/systems/user_commands/AvatarCommand.java b/src/main/java/net/javadiscord/javabot/systems/user_commands/AvatarCommand.java index 0074d3f60..6da88a8f4 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_commands/AvatarCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_commands/AvatarCommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.user_commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; @@ -38,7 +39,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { private @NotNull MessageEmbed buildAvatarEmbed(@NotNull User createdBy) { return new EmbedBuilder() .setColor(Responses.Type.DEFAULT.getColor()) - .setAuthor(createdBy.getAsTag(), null, createdBy.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(createdBy), null, createdBy.getEffectiveAvatarUrl()) .setTitle("Avatar") .setImage(createdBy.getEffectiveAvatarUrl() + "?size=4096") .build(); diff --git a/src/main/java/net/javadiscord/javabot/systems/user_commands/BotInfoCommand.java b/src/main/java/net/javadiscord/javabot/systems/user_commands/BotInfoCommand.java index 3deb68232..2c34eedfc 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_commands/BotInfoCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_commands/BotInfoCommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.user_commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.JDA; @@ -39,7 +40,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { return new EmbedBuilder() .setColor(Responses.Type.DEFAULT.getColor()) .setThumbnail(jda.getSelfUser().getEffectiveAvatarUrl()) - .setAuthor(jda.getSelfUser().getAsTag(), null, jda.getSelfUser().getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(jda.getSelfUser()), null, jda.getSelfUser().getEffectiveAvatarUrl()) .setTitle("Bot Information") .addField("OS", MarkdownUtil.codeblock(StringUtils.getOperatingSystem()), true) .addField("Uptime", MarkdownUtil.codeblock(StringUtils.formatUptime()), true) diff --git a/src/main/java/net/javadiscord/javabot/systems/user_commands/ChangeMyMindCommand.java b/src/main/java/net/javadiscord/javabot/systems/user_commands/ChangeMyMindCommand.java index f04b7899b..7ad10efea 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_commands/ChangeMyMindCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_commands/ChangeMyMindCommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.user_commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.JsonNode; @@ -56,7 +57,7 @@ public void completed(HttpResponse hr) { try { String imageUrl = hr.getBody().getObject().getString("message"); event.getHook().sendMessageEmbeds(new EmbedBuilder() - .setAuthor(event.getUser().getAsTag(), imageUrl, event.getUser().getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(event.getUser()), imageUrl, event.getUser().getEffectiveAvatarUrl()) .setColor(Responses.Type.DEFAULT.getColor()) .setImage(imageUrl) .setTimestamp(Instant.now()) diff --git a/src/main/java/net/javadiscord/javabot/systems/user_commands/IdCalculatorCommand.java b/src/main/java/net/javadiscord/javabot/systems/user_commands/IdCalculatorCommand.java index 5cf637550..50753864c 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_commands/IdCalculatorCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_commands/IdCalculatorCommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.user_commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; @@ -50,7 +51,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { private @NotNull MessageEmbed buildIdCalcEmbed(@NotNull User author, long id, long unixTimestamp) { return new EmbedBuilder() - .setAuthor(author.getAsTag(), null, author.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(author), null, author.getEffectiveAvatarUrl()) .setTitle("ID Calculator") .setColor(Responses.Type.DEFAULT.getColor()) .addField("Snowflake Input", String.format(MarkdownUtil.codeblock("%s"), id), false) diff --git a/src/main/java/net/javadiscord/javabot/systems/user_commands/PollCommand.java b/src/main/java/net/javadiscord/javabot/systems/user_commands/PollCommand.java index 5c886cd2e..bbdee613d 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_commands/PollCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_commands/PollCommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.user_commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.emoji.Emoji; @@ -52,7 +53,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { return; } EmbedBuilder embed = new EmbedBuilder() - .setAuthor(event.getUser().getAsTag(), null, event.getUser().getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(event.getUser()), null, event.getUser().getEffectiveAvatarUrl()) .setTitle(titleOption.getAsString()) .setColor(Responses.Type.DEFAULT.getColor()) .setTimestamp(Instant.now()); diff --git a/src/main/java/net/javadiscord/javabot/systems/user_commands/ProfileCommand.java b/src/main/java/net/javadiscord/javabot/systems/user_commands/ProfileCommand.java index dd70ae913..355fdedda 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_commands/ProfileCommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_commands/ProfileCommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.user_commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.*; @@ -86,7 +87,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { double helpXP = helpExperienceService.getOrCreateAccount(member.getIdLong()).getExperience(); EmbedBuilder embed = new EmbedBuilder() .setTitle("Profile") - .setAuthor(member.getUser().getAsTag(), null, member.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(member.getUser()), null, member.getEffectiveAvatarUrl()) .setDescription(getDescription(member)) .setColor(member.getColor()) .setThumbnail(member.getEffectiveAvatarUrl() + "?size=4096") diff --git a/src/main/java/net/javadiscord/javabot/systems/user_commands/leaderboard/ExperienceLeaderboardSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/user_commands/leaderboard/ExperienceLeaderboardSubcommand.java index 6e8acaa3c..65eeb659b 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_commands/leaderboard/ExperienceLeaderboardSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_commands/leaderboard/ExperienceLeaderboardSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.user_commands.leaderboard; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.util.ComponentIdBuilder; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import xyz.dynxsty.dih4jda.interactions.components.ButtonHandler; @@ -90,7 +91,7 @@ public void handleButton(@NotNull ButtonInteractionEvent event, Button button) { Pair currentRole = account.getCurrentExperienceGoal(guild); User user = guild.getJDA().getUserById(account.getUserId()); builder.addField( - String.format("**%s.** %s", (accounts.indexOf(account) + 1) + (page - 1) * PAGE_SIZE, user == null ? account.getUserId() : user.getAsTag()), + String.format("**%s.** %s", (accounts.indexOf(account) + 1) + (page - 1) * PAGE_SIZE, user == null ? account.getUserId() : UserUtils.getUserTag(user)), String.format("%s`%.0f XP`\n", currentRole.first() != null ? currentRole.first().getAsMention() + ": " : "", account.getExperience()), false); }); diff --git a/src/main/java/net/javadiscord/javabot/systems/user_commands/leaderboard/QOTWLeaderboardSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/user_commands/leaderboard/QOTWLeaderboardSubcommand.java index b43029234..d54120106 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_commands/leaderboard/QOTWLeaderboardSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_commands/leaderboard/QOTWLeaderboardSubcommand.java @@ -14,6 +14,7 @@ import javax.imageio.ImageIO; +import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import org.springframework.dao.DataAccessException; @@ -104,7 +105,7 @@ private MessageEmbed buildLeaderboardRankEmbed(Member member) { long points = pointsService.getPoints(member.getIdLong()); String pointsText = points == 1 ? "point" : "points"; return new EmbedBuilder() - .setAuthor(member.getUser().getAsTag(), null, member.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(member.getUser()), null, member.getEffectiveAvatarUrl()) .setTitle("Question of the Week Leaderboard") .setDescription(points == 0 ? "You are currently not ranked." : String.format("This month, you're in `%s` place with `%s` %s.", rank + rankSuffix, points, pointsText)) @@ -124,9 +125,9 @@ private MessageEmbed buildLeaderboardRankEmbed(Member member) { */ private void drawUserCard(@NotNull Graphics2D g2d, @NotNull Member member, QOTWPointsService service, int y, boolean left) throws IOException { BufferedImage card = ImageGenerationUtils.getResourceImage("assets/images/LeaderboardUserCard.png"); - int x = left ? MARGIN * 5 : WIDTH - (MARGIN * 5) - card.getWidth(); + int x = left ? MARGIN * 5 : WIDTH - MARGIN * 5 - card.getWidth(); g2d.drawImage(ImageGenerationUtils.getImageFromUrl(member.getEffectiveAvatarUrl() + "?size=4096"), x + 185, y + 43, 200, 200, null); - String displayName = member.getUser().getAsTag(); + String displayName = UserUtils.getUserTag(member.getUser()); // draw card g2d.drawImage(card, x, y, null); g2d.setColor(PRIMARY_COLOR); diff --git a/src/main/java/net/javadiscord/javabot/systems/user_preferences/commands/PreferencesListSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/user_preferences/commands/PreferencesListSubcommand.java index 7f1326b85..6586c255c 100644 --- a/src/main/java/net/javadiscord/javabot/systems/user_preferences/commands/PreferencesListSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/user_preferences/commands/PreferencesListSubcommand.java @@ -1,5 +1,6 @@ package net.javadiscord.javabot.systems.user_preferences.commands; +import net.javadiscord.javabot.util.UserUtils; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; @@ -35,7 +36,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) { private @NotNull MessageEmbed buildPreferencesEmbed(UserPreferenceService service, @NotNull User user) { EmbedBuilder builder = new EmbedBuilder() - .setAuthor(user.getAsTag(), null, user.getEffectiveAvatarUrl()) + .setAuthor(UserUtils.getUserTag(user), null, user.getEffectiveAvatarUrl()) .setTitle(user.getName() + "'s Preferences") .setColor(Responses.Type.INFO.getColor()); for (Preference p : Preference.values()) { diff --git a/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java b/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java index d0d2886ff..bd43e2b71 100644 --- a/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java +++ b/src/main/java/net/javadiscord/javabot/util/InteractionUtils.java @@ -111,7 +111,7 @@ private void kick(ModalInteraction interaction, @NotNull Guild guild, String mem guild.retrieveMemberById(memberId).queue( member -> { service.kick(member.getUser(), reason, interaction.getMember(), interaction.getMessageChannel(), false); - interaction.getMessage().editMessageComponents(ActionRow.of(Button.danger(interaction.getModalId(), "Kicked by "+interaction.getUser().getAsTag()).asDisabled())).queue(); + interaction.getMessage().editMessageComponents(ActionRow.of(Button.danger(interaction.getModalId(), "Kicked by " + UserUtils.getUserTag(interaction.getUser())).asDisabled())).queue(); }, error -> Responses.error(interaction.getHook(), "Could not find member: " + error.getMessage()).queue() ); } @@ -125,7 +125,7 @@ private void warn(ModalInteraction interaction, @NotNull Guild guild, String mem guild.retrieveMemberById(memberId).queue( member -> { service.warn(member.getUser(), severity, reason, interaction.getMember(), interaction.getMessageChannel(), false); - interaction.getHook().editOriginalComponents(ActionRow.of(Button.primary(interaction.getModalId(), "Warned by "+interaction.getUser().getAsTag()).asDisabled())).queue(); + interaction.getHook().editOriginalComponents(ActionRow.of(Button.primary(interaction.getModalId(), "Warned by " + UserUtils.getUserTag(interaction.getUser())).asDisabled())).queue(); }, error -> Responses.error(interaction.getHook(), "Could not find member: " + error.getMessage()).queue() ); } @@ -139,7 +139,7 @@ private void ban(ModalInteraction interaction, @NotNull Guild guild, String memb guild.getJDA().retrieveUserById(memberId).queue( user -> { service.ban(user, reason, interaction.getMember(), interaction.getMessageChannel(), false); - interaction.getMessage().editMessageComponents(ActionRow.of(Button.danger(interaction.getModalId(), "Banned by "+interaction.getUser().getAsTag()).asDisabled())).queue(); + interaction.getMessage().editMessageComponents(ActionRow.of(Button.danger(interaction.getModalId(), "Banned by " + UserUtils.getUserTag(interaction.getUser())).asDisabled())).queue(); }, error -> Responses.error(interaction.getHook(), "Could not find member: " + error.getMessage()).queue() ); } @@ -151,7 +151,7 @@ private void unban(ModalInteraction interaction, long memberId, String reason) { } ModerationService service = new ModerationService(notificationService, botConfig, interaction, warnRepository, asyncPool); service.unban(memberId, reason, interaction.getMember(), interaction.getMessageChannel(), false); - interaction.getMessage().editMessageComponents(ActionRow.of(Button.secondary(interaction.getModalId(), "Unbanned by "+interaction.getUser().getAsTag()).asDisabled())).queue(); + interaction.getMessage().editMessageComponents(ActionRow.of(Button.secondary(interaction.getModalId(), "Unbanned by " + UserUtils.getUserTag(interaction.getUser())).asDisabled())).queue(); } @Override diff --git a/src/main/java/net/javadiscord/javabot/util/UserUtils.java b/src/main/java/net/javadiscord/javabot/util/UserUtils.java new file mode 100644 index 000000000..7190db7d3 --- /dev/null +++ b/src/main/java/net/javadiscord/javabot/util/UserUtils.java @@ -0,0 +1,25 @@ +package net.javadiscord.javabot.util; + +import net.dv8tion.jda.api.entities.User; + +/** + * Utils regarding users. + */ +public class UserUtils { + /** + * Returns the tag of a discord user, in the format "[username]#[discriminator]", or just "[username]" if the new username system is used. + * + * @param user The {@link User} to get the tag of + * + * @return The formatted tag of the user + */ + public static String getUserTag(User user) { + String name = user.getName(); + String discrim = user.getDiscriminator(); + if ("0000".equals(discrim)) { + return name; + } else { + return name + "#" + discrim; + } + } +} From 3486836600e0196650181222789bc381b8c9fc68 Mon Sep 17 00:00:00 2001 From: 0x3C50 <99053360+0x3C50@users.noreply.github.com> Date: Sun, 18 Jun 2023 17:53:37 +0200 Subject: [PATCH 036/351] Update jda to 5.0.0-beta.10 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index e807fdffc..c45f1e134 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,7 +31,7 @@ dependencies { // DIH4JDA (Command Framework) & JDA implementation("xyz.dynxsty:dih4jda:1.6.2") - implementation("net.dv8tion:JDA:5.0.0-beta.2") { + implementation("net.dv8tion:JDA:5.0.0-beta.10") { exclude(module = "opus-java") } From 2a99735b790ca5b1dbb51fade5f4d5eda3aa262e Mon Sep 17 00:00:00 2001 From: danthe1st Date: Fri, 4 Aug 2023 16:06:27 +0200 Subject: [PATCH 037/351] remove unnecessary import --- .../javabot/systems/help/commands/HelpPingSubcommand.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java index d11954c33..ec5b26361 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/commands/HelpPingSubcommand.java @@ -20,7 +20,6 @@ import net.javadiscord.javabot.data.config.guild.HelpConfig; import net.javadiscord.javabot.util.Pair; import net.javadiscord.javabot.util.Responses; -import net.javadiscord.javabot.util.UserUtils; import org.jetbrains.annotations.NotNull; import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand; import xyz.dynxsty.dih4jda.interactions.components.ButtonHandler; From bb55df309d7aeaeba138d1875349bde17efeea73 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Fri, 4 Aug 2023 16:26:53 +0200 Subject: [PATCH 038/351] use addActionRow for adding single ActionRow --- .../javabot/systems/moderation/report/ReportManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java b/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java index 4113d706b..31421ad71 100644 --- a/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java +++ b/src/main/java/net/javadiscord/javabot/systems/moderation/report/ReportManager.java @@ -110,7 +110,7 @@ protected Modal buildMessageReportModal(@NotNull MessageContextInteractionEvent .setMaxLength(MessageEmbed.VALUE_MAX_LENGTH) .build(); return Modal.create(ComponentIdBuilder.build("report", "message", event.getTarget().getId()), title.substring(0, Math.min(title.length(), Modal.MAX_TITLE_LENGTH))) - .addComponents(ActionRow.of(messageInput)) + .addActionRow(messageInput) .build(); } From ceb2f5431ebd6edf4ca2bada6e00342f4f56b129 Mon Sep 17 00:00:00 2001 From: danthe1st Date: Sun, 6 Aug 2023 15:24:28 +0200 Subject: [PATCH 039/351] add missing Spring annotation --- .../javadiscord/javabot/systems/help/model/HelpAccount.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/javadiscord/javabot/systems/help/model/HelpAccount.java b/src/main/java/net/javadiscord/javabot/systems/help/model/HelpAccount.java index 77f11197c..801c68dff 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/model/HelpAccount.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/model/HelpAccount.java @@ -9,6 +9,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import com.fasterxml.jackson.annotation.JsonIgnore; + import java.util.Comparator; import java.util.Map; import java.util.Optional; @@ -19,6 +21,7 @@ @Data @RequiredArgsConstructor public class HelpAccount { + @JsonIgnore private final BotConfig botConfig; private long userId; private double experience; From eef08747fd5554c80250ab1d645615c14d7b6691 Mon Sep 17 00:00:00 2001 From: Pleezon Date: Mon, 7 Aug 2023 16:29:38 +0200 Subject: [PATCH 040/351] initial commit: - added AutoCodeFormatter.java - added a preference for enabling/disabling AutoCodeFormatter - added a Setting in HelpConfig: hint for when users don't format their code - added a Setting in HelpConfig: footnote for when code has been autoformatted - edited HelpListener to include a call to AutoCodeFormatter.java - moved replaceMemberMessage from HugListener to WebhookUtil to allow usage from within AutoCodeFormatter.java --- .../javabot/data/config/guild/HelpConfig.java | 230 +++++----- .../javabot/listener/HugListener.java | 160 ++++--- .../systems/help/AutoCodeFormatter.java | 92 ++++ .../javabot/systems/help/HelpListener.java | 419 +++++++++--------- .../user_preferences/model/Preference.java | 61 +-- .../javadiscord/javabot/util/WebhookUtil.java | 7 + 6 files changed, 542 insertions(+), 427 deletions(-) create mode 100644 src/main/java/net/javadiscord/javabot/systems/help/AutoCodeFormatter.java diff --git a/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java b/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java index 27957d9ac..9b910a4f7 100644 --- a/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java +++ b/src/main/java/net/javadiscord/javabot/data/config/guild/HelpConfig.java @@ -15,113 +15,125 @@ @Data @EqualsAndHashCode(callSuper = true) public class HelpConfig extends GuildConfigItem { - private long helpForumChannelId = 0; - - /** - * The id of the helper role. - */ - private long helperRoleId; - - /** - * The id of the help-ping role. - */ - private long helpNotificationChannelId; - - /** - * The message that's sent as soon as a user asks a question in an open help - * channel. This is only sent if it's not null. - */ - private String reservedChannelMessageTemplate = "`⌛` **This post has been reserved for your question.**\n> Hey %s! Please use `/close` or the `Close Post` button above when you're finished. Please remember to follow the help guidelines. This post will be automatically closed after %s minutes of inactivity.\n\n**TIP:** Narrow down your issue to __simple__ and __precise__ questions to maximize the chance that others will reply in here."; - - /** - * The message that's sent in a post to tell users that it - * is now marked as dormant and no more messages can be sent. - */ - private String dormantChannelMessageTemplate = "`\uD83D\uDCA4` **Post marked as dormant**\n> This post has been inactive for over %s minutes, thus, it has been **archived**.\n> If your question was not answered yet, feel free to re-open this post or create a new one."; - - /** - * The message that's sent in a post to tell users that it - * is now marked as dormant and no more messages can be sent. - */ - private String dormantChannelPrivateMessageTemplate = """ - Your post %s in %s has been inactive for over %s minutes, thus, it has been **archived**. - If your question was not answered yet, feel free to re-open this post by sending another message in that channel. - You can disable notifications like this using the `/preferences` command. - [Post link](%s) - """; - - - /** - * The message that's sent when a user unreserved a channel where other users - * participated in. - */ - private String helpThanksMessageTemplate = "Before your post will be closed, would you like to express your gratitude to any of the people who helped you? When you're done, click **I'm done here. Close this post!**."; - - /** - * The number of minutes of inactivity before a channel is considered inactive. - */ - private int inactivityTimeoutMinutes = 300; - - /** - * The number of minutes to wait before closing a channel waiting for a response - * to a thanks question. - */ - private int removeThanksTimeoutMinutes = 10; - - /** - * How often users may use the /help-ping command. - */ - private int helpPingTimeoutSeconds = 300; - - /** - * The maximum amount of experience one can get from one help channel. - */ - private double maxExperiencePerChannel = 50; - - /** - * The base experience one gets for every message. - */ - private double baseExperience = 5; - - /** - * The weight for each character. - */ - private double perCharacterExperience = 1; - - /** - * The messages' minimum length. - */ - private int minimumMessageLength = 10; - - /** - * The amount of experience points one gets for being thanked by the help channel owner. - */ - private double thankedExperience = 50; - - /** - * The amount of experience one gets for thanking other users. - */ - private double thankExperience = 3; - - /** - * The amount that should be subtracted from every Help Account each day. - */ - private double dailyExperienceSubtraction = 5; - - /** - * A list with all roles that are awarded by experience. - */ - private Map experienceRoles = Map.of(0L, 0.0); - - public ForumChannel getHelpForumChannel() { - return getGuild().getForumChannelById(helpForumChannelId); - } - - public Role getHelperRole() { - return getGuild().getRoleById(helperRoleId); - } - - public TextChannel getHelpNotificationChannel() { - return getGuild().getTextChannelById(helpNotificationChannelId); - } + private long helpForumChannelId = 0; + + /** + * The id of the helper role. + */ + private long helperRoleId; + + /** + * The id of the help-ping role. + */ + private long helpNotificationChannelId; + + /** + * The message that's sent as soon as a user asks a question in an open help + * channel. This is only sent if it's not null. + */ + private String reservedChannelMessageTemplate = "`⌛` **This post has been reserved for your question.**\n> Hey %s! Please use `/close` or the `Close Post` button above when you're finished. Please remember to follow the help guidelines. This post will be automatically closed after %s minutes of inactivity.\n\n**TIP:** Narrow down your issue to __simple__ and __precise__ questions to maximize the chance that others will reply in here."; + + /** + * The message that's sent in a post to tell users that it + * is now marked as dormant and no more messages can be sent. + */ + private String dormantChannelMessageTemplate = "`\uD83D\uDCA4` **Post marked as dormant**\n> This post has been inactive for over %s minutes, thus, it has been **archived**.\n> If your question was not answered yet, feel free to re-open this post or create a new one."; + + /** + * The message that's sent in a post to tell users that it + * is now marked as dormant and no more messages can be sent. + */ + private String dormantChannelPrivateMessageTemplate = """ + Your post %s in %s has been inactive for over %s minutes, thus, it has been **archived**. + If your question was not answered yet, feel free to re-open this post by sending another message in that channel. + You can disable notifications like this using the `/preferences` command. + [Post link](%s) + """; + + /** + * The message that is sent in a post to tell users that they + * should use discord's code-formatting, provided the bot detects unformatted code. + * Issued by {@link net.javadiscord.javabot.systems.help.AutoCodeFormatter} + */ + private String formatHintMessage = "> Please format your code to make it more readable. \n> For java, it should look like this: \n```\u200B`\u200B`\u200B`\u200Bjava\npublic void foo() {\n \n}\u200B`\u200B`\u200B`\u200B```"; + + /** + * The message that's sent when a user unreserved a channel where other users + * participated in. + */ + private String helpThanksMessageTemplate = "Before your post will be closed, would you like to express your gratitude to any of the people who helped you? When you're done, click **I'm done here. Close this post!**."; + + /** + * The number of minutes of inactivity before a channel is considered inactive. + */ + private int inactivityTimeoutMinutes = 300; + + /** + * The number of minutes to wait before closing a channel waiting for a response + * to a thanks question. + */ + private int removeThanksTimeoutMinutes = 10; + + /** + * How often users may use the /help-ping command. + */ + private int helpPingTimeoutSeconds = 300; + + /** + * The maximum amount of experience one can get from one help channel. + */ + private double maxExperiencePerChannel = 50; + + /** + * The base experience one gets for every message. + */ + private double baseExperience = 5; + + /** + * The weight for each character. + */ + private double perCharacterExperience = 1; + + /** + * The messages' minimum length. + */ + private int minimumMessageLength = 10; + + /** + * The message-embed's footnote of an unformatted-code-replacement. + * Issued by {@link net.javadiscord.javabot.systems.help.AutoCodeFormatter} + */ + private String autoformatInfoMessage = "This message has been formatted automatically."; + + /** + * The amount of experience points one gets for being thanked by the help channel owner. + */ + private double thankedExperience = 50; + + /** + * The amount of experience one gets for thanking other users. + */ + private double thankExperience = 3; + + /** + * The amount that should be subtracted from every Help Account each day. + */ + private double dailyExperienceSubtraction = 5; + + /** + * A list with all roles that are awarded by experience. + */ + private Map experienceRoles = Map.of(0L, 0.0); + + public ForumChannel getHelpForumChannel() { + return getGuild().getForumChannelById(helpForumChannelId); + } + + public Role getHelperRole() { + return getGuild().getRoleById(helperRoleId); + } + + public TextChannel getHelpNotificationChannel() { + return getGuild().getTextChannelById(helpNotificationChannelId); + } } diff --git a/src/main/java/net/javadiscord/javabot/listener/HugListener.java b/src/main/java/net/javadiscord/javabot/listener/HugListener.java index 165df00ed..c238091ef 100644 --- a/src/main/java/net/javadiscord/javabot/listener/HugListener.java +++ b/src/main/java/net/javadiscord/javabot/listener/HugListener.java @@ -2,8 +2,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.Webhook; import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.middleman.StandardGuildChannel; @@ -24,92 +22,86 @@ @Slf4j @RequiredArgsConstructor public class HugListener extends ListenerAdapter { - private static final Pattern FUCKER = Pattern.compile("(fuck)(ing|er|ed|k+)?", Pattern.CASE_INSENSITIVE); - private final AutoMod autoMod; - private final BotConfig botConfig; + private static final Pattern FUCKER = Pattern.compile("(fuck)(ing|er|ed|k+)?", Pattern.CASE_INSENSITIVE); + private final AutoMod autoMod; + private final BotConfig botConfig; - @Override - public void onMessageReceived(@Nonnull MessageReceivedEvent event) { - if (!event.isFromGuild()) { - return; - } - if (autoMod.hasSuspiciousLink(event.getMessage()) || autoMod.hasAdvertisingLink(event.getMessage())) { - return; - } - if (!event.getMessage().getMentions().getUsers().isEmpty()) { - return; - } - if (event.isWebhookMessage()) { - return; - } - if (event.getChannel().getIdLong() == botConfig.get(event.getGuild()).getModerationConfig() - .getSuggestionChannelId()) { - return; - } - TextChannel tc = null; - if (event.isFromType(ChannelType.TEXT)) { - tc = event.getChannel().asTextChannel(); - } - if (event.isFromThread()) { - StandardGuildChannel parentChannel = event.getChannel().asThreadChannel().getParentChannel().asStandardGuildChannel(); - if (parentChannel instanceof TextChannel textChannel) { - tc = textChannel; - } - } - if (tc == null) { - return; - } - String content = event.getMessage().getContentRaw(); - if (FUCKER.matcher(content).find()) { - long threadId = event.isFromThread() ? event.getChannel().getIdLong() : 0; - WebhookUtil.ensureWebhookExists(tc, - wh -> sendWebhookMessage(wh, event.getMessage(), replaceFucks(content), threadId), - e -> ExceptionLogger.capture(e, getClass().getSimpleName())); - } - } + private static String processHug(String originalText) { + // FucK -> HuG, FuCk -> Hug + return String.valueOf(copyCase(originalText, 0, 'h')) + + copyCase(originalText, 1, 'u') + + copyCase(originalText, 3, 'g'); + } - private static String processHug(String originalText) { - // FucK -> HuG, FuCk -> Hug - return String.valueOf(copyCase(originalText, 0, 'h')) - + copyCase(originalText, 1, 'u') - + copyCase(originalText, 3, 'g'); - } + private static String replaceFucks(String str) { + return FUCKER.matcher(str).replaceAll(matchResult -> { + String theFuck = matchResult.group(1); + String suffix = Objects.requireNonNullElse(matchResult.group(2), ""); + String processedSuffix = switch (suffix.toLowerCase()) { + case "er", "ed", "ing" -> copyCase(suffix, 0, 'g') + suffix; // fucking, fucker, fucked + case "" -> ""; // just fuck + default -> copyCase(suffix, "g".repeat(suffix.length())); // fuckkkkk... + }; + return processHug(theFuck) + processedSuffix; + }); + } - private static String replaceFucks(String str) { - return FUCKER.matcher(str).replaceAll(matchResult -> { - String theFuck = matchResult.group(1); - String suffix = Objects.requireNonNullElse(matchResult.group(2), ""); - String processedSuffix = switch(suffix.toLowerCase()) { - case "er", "ed", "ing" -> copyCase(suffix, 0, 'g') + suffix; // fucking, fucker, fucked - case "" -> ""; // just fuck - default -> copyCase(suffix, "g".repeat(suffix.length())); // fuckkkkk... - }; - return processHug(theFuck) + processedSuffix; - }); - } + private static String copyCase(String source, String toChange) { + if (source.length() != toChange.length()) throw new IllegalArgumentException("lengths differ"); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < source.length(); i++) { + sb.append(copyCase(source, i, toChange.charAt(i))); + } + return sb.toString(); + } - private static String copyCase(String source, String toChange) { - if (source.length() != toChange.length()) throw new IllegalArgumentException("lengths differ"); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < source.length(); i++) { - sb.append(copyCase(source, i, toChange.charAt(i))); - } - return sb.toString(); - } + private static char copyCase(String original, int index, char newChar) { + if (Character.isUpperCase(original.charAt(index))) { + return Character.toUpperCase(newChar); + } else { + return newChar; + } + } + + @Override + public void onMessageReceived(@Nonnull MessageReceivedEvent event) { + if (!event.isFromGuild()) { + return; + } + if (autoMod.hasSuspiciousLink(event.getMessage()) || autoMod.hasAdvertisingLink(event.getMessage())) { + return; + } + if (!event.getMessage().getMentions().getUsers().isEmpty()) { + return; + } + if (event.isWebhookMessage()) { + return; + } + if (event.getChannel().getIdLong() == botConfig.get(event.getGuild()).getModerationConfig() + .getSuggestionChannelId()) { + return; + } + TextChannel tc = null; + if (event.isFromType(ChannelType.TEXT)) { + tc = event.getChannel().asTextChannel(); + } + if (event.isFromThread()) { + StandardGuildChannel parentChannel = event.getChannel().asThreadChannel().getParentChannel().asStandardGuildChannel(); + if (parentChannel instanceof TextChannel textChannel) { + tc = textChannel; + } + } + if (tc == null) { + return; + } + String content = event.getMessage().getContentRaw(); + if (FUCKER.matcher(content).find()) { + long threadId = event.isFromThread() ? event.getChannel().getIdLong() : 0; + WebhookUtil.ensureWebhookExists(tc, + wh -> WebhookUtil.replaceMemberMessage(wh, event.getMessage(), replaceFucks(content), threadId), + e -> ExceptionLogger.capture(e, getClass().getSimpleName())); + } + } - private static char copyCase(String original, int index, char newChar) { - if (Character.isUpperCase(original.charAt(index))) { - return Character.toUpperCase(newChar); - } else { - return newChar; - } - } - private void sendWebhookMessage(Webhook webhook, Message originalMessage, String newMessageContent, long threadId) { - WebhookUtil.mirrorMessageToWebhook(webhook, originalMessage, newMessageContent, threadId, null, null) - .thenAccept(unused -> originalMessage.delete().queue()).exceptionally(e -> { - ExceptionLogger.capture(e, getClass().getSimpleName()); - return null; - }); - } } diff --git a/src/main/java/net/javadiscord/javabot/systems/help/AutoCodeFormatter.java b/src/main/java/net/javadiscord/javabot/systems/help/AutoCodeFormatter.java new file mode 100644 index 000000000..7cb2a1b1e --- /dev/null +++ b/src/main/java/net/javadiscord/javabot/systems/help/AutoCodeFormatter.java @@ -0,0 +1,92 @@ +package net.javadiscord.javabot.systems.help; + +import lombok.AllArgsConstructor; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.javadiscord.javabot.data.config.BotConfig; +import net.javadiscord.javabot.systems.moderation.AutoMod; +import net.javadiscord.javabot.systems.user_preferences.UserPreferenceService; +import net.javadiscord.javabot.systems.user_preferences.model.Preference; +import net.javadiscord.javabot.util.ExceptionLogger; +import net.javadiscord.javabot.util.WebhookUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.Nonnull; +import java.util.Objects; + +/** + * Handles auto-formatting of code in help-channels. + * A non-listener class since it has to be called strictly after {@link HelpListener} to not send its messages before channel reserving. + */ +@AllArgsConstructor +public class AutoCodeFormatter { + private final AutoMod autoMod; + private final BotConfig botConfig; + private final UserPreferenceService preferenceService; + + /** + * Method responsible for finding a codeblock, if present. + * + * @return a MessageCodeblock instance, holding a startIndex, content and an endIndex + */ + @Nullable + private static AutoCodeFormatter.CodeBlock findCodeblock(@NotNull MessageReceivedEvent event) { + String msg = event.getMessage().getContentRaw(); + int openingBracket = msg.indexOf("{"); + int closingBracket = msg.lastIndexOf("}"); + if (closingBracket == -1 || openingBracket == -1) return null; + int startIndex = msg.lastIndexOf("\n", openingBracket); + int endIndex = msg.indexOf("\n", closingBracket); + if (startIndex == -1) startIndex = 0; + if (endIndex == -1) endIndex = msg.length(); + return new CodeBlock(startIndex, endIndex); + } + + public void handleMessageEvent(@Nonnull MessageReceivedEvent event) { + if (!event.isFromGuild()) return; + if (event.getAuthor().isBot() || event.getMessage().getAuthor().isSystem()) return; + if (autoMod.hasSuspiciousLink(event.getMessage()) || autoMod.hasAdvertisingLink(event.getMessage())) return; + if (event.isWebhookMessage()) return; + if (!event.isFromThread()) return; + if (event.getChannel().asThreadChannel().getParentChannel().getIdLong() != botConfig.get(event.getGuild()).getHelpConfig().getHelpForumChannelId()) + return; + if (!Boolean.parseBoolean(preferenceService.getOrCreate(Objects.requireNonNull(event.getMember()).getIdLong(), Preference.FORMAT_UNFORMATTED_CODE).getState())) + return; + + + if (event.getMessage().getContentRaw().contains("```")) return; // exit if already contains codeblock + + CodeBlock code = findCodeblock(event); + if (code == null) return; + + if (event.getMessage().getMentions().getUsers().isEmpty() && event.getChannel().asThreadChannel().getTotalMessageCount() > 1) + replaceUnformattedCode(event.getMessage().getContentRaw(), code.startIndex(), code.endIndex(), event); + else sendFormatHint(event); + } + + private void sendFormatHint(MessageReceivedEvent event) { + event.getChannel().sendMessageEmbeds(formatHintEmbed(event.getGuild())).queue(); + } + + private void replaceUnformattedCode(String msg, int codeStartIndex, int codeEndIndex, MessageReceivedEvent event) { + // default case: a "normal", non-ping containing, non first message of a forum-thread containing "{" and "}". + // user must also have set their preferences to allow this. + if (event.getMessage().getContentRaw().length() > 1992) { // can't exceed discord's char limit + sendFormatHint(event); + return; + } + String messageContent = msg.substring(0, codeStartIndex) + " ```" + msg.substring(codeStartIndex, codeEndIndex) + " ```" + msg.substring(codeEndIndex); + EmbedBuilder autoformatInfo = new EmbedBuilder().setDescription(botConfig.get(event.getGuild()).getHelpConfig().getAutoformatInfoMessage()); + WebhookUtil.ensureWebhookExists(event.getChannel().asThreadChannel().getParentChannel().asForumChannel(), (wh) -> WebhookUtil.replaceMemberMessage(wh, event.getMessage(), messageContent, event.getChannel().getIdLong(), autoformatInfo.build(), formatHintEmbed(event.getGuild())), (e) -> ExceptionLogger.capture(e, "Error creating webhook for UnformattedCodeListener")); + } + + private MessageEmbed formatHintEmbed(Guild guild) { + return new EmbedBuilder().setDescription(botConfig.get(guild).getHelpConfig().getFormatHintMessage()).build(); + } + + private record CodeBlock(int startIndex, int endIndex) { + } +} diff --git a/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java b/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java index e77f8e2ec..d72e30581 100644 --- a/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java +++ b/src/main/java/net/javadiscord/javabot/systems/help/HelpListener.java @@ -1,6 +1,5 @@ package net.javadiscord.javabot.systems.help; -import lombok.RequiredArgsConstructor; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.UserSnowflake; @@ -22,6 +21,7 @@ import net.javadiscord.javabot.data.h2db.DbActions; import net.javadiscord.javabot.systems.help.dao.HelpAccountRepository; import net.javadiscord.javabot.systems.help.dao.HelpTransactionRepository; +import net.javadiscord.javabot.systems.moderation.AutoMod; import net.javadiscord.javabot.systems.user_preferences.UserPreferenceService; import net.javadiscord.javabot.util.InteractionUtils; import net.javadiscord.javabot.util.Responses; @@ -29,228 +29,239 @@ import xyz.dynxsty.dih4jda.interactions.components.ButtonHandler; import xyz.dynxsty.dih4jda.util.ComponentIdBuilder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * Listens for all events related to the forum help channel system. */ -@RequiredArgsConstructor + @AutoDetectableComponentHandler({HelpManager.HELP_THANKS_IDENTIFIER, HelpManager.HELP_CLOSE_IDENTIFIER, HelpManager.HELP_GUIDELINES_IDENTIFIER}) public class HelpListener extends ListenerAdapter implements ButtonHandler { - /** - * A static Map that holds all messages that was sent in a specific reserved forum channel. - */ - static final Map> HELP_POST_MESSAGES = new HashMap<>(); - private static final Set newThreadChannels; + /** + * A static Map that holds all messages that was sent in a specific reserved forum channel. + */ + static final Map> HELP_POST_MESSAGES = new HashMap<>(); + private static final Set newThreadChannels; + + static { + newThreadChannels = new HashSet<>(); + } + + private final UserPreferenceService preferenceService; + private final BotConfig botConfig; + private final HelpAccountRepository helpAccountRepository; + private final HelpTransactionRepository helpTransactionRepository; + private final HelpExperienceService experienceService; + private final DbActions dbActions; + private final String[][] closeSuggestionDetectors = { + {"close", "post"}, + {"close", "thread"}, + {"close", "question"}, + {"problem", "solv"}, + {"issue", "solv"}, + {"thank"} + }; + private final long SUGGEST_CLOSE_TIMEOUT = 5 * 60_000L;//5 minutes + private final Map recentlyCloseSuggestedPosts = new LinkedHashMap<>(8, 0.75f, true) { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return System.currentTimeMillis() > eldest.getValue() || size() >= 32; + } + }; + private final AutoCodeFormatter autoCodeFormatter; - static { - newThreadChannels = new HashSet<>(); - } + public HelpListener(AutoMod autoMod, UserPreferenceService preferenceService, BotConfig botConfig, HelpAccountRepository helpAccountRepository, HelpTransactionRepository helpTransactionRepository, HelpExperienceService experienceService, DbActions dbActions) { + this.preferenceService = preferenceService; + this.botConfig = botConfig; + this.helpAccountRepository = helpAccountRepository; + this.helpTransactionRepository = helpTransactionRepository; + this.experienceService = experienceService; + this.dbActions = dbActions; + this.autoCodeFormatter = new AutoCodeFormatter(autoMod, botConfig, preferenceService); + } - private final BotConfig botConfig; - private final HelpAccountRepository helpAccountRepository; - private final HelpTransactionRepository helpTransactionRepository; - private final HelpExperienceService experienceService; - private final DbActions dbActions; - private final String[][] closeSuggestionDetectors = { - {"close", "post"}, - {"close", "thread"}, - {"close", "question"}, - {"problem","solv"}, - {"issue","solv"}, - {"thank"} - }; - private final long SUGGEST_CLOSE_TIMEOUT = 5 * 60_000L;//5 minutes - private final Map recentlyCloseSuggestedPosts = new LinkedHashMap<>(8, 0.75f, true) { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return System.currentTimeMillis() > eldest.getValue() || size() >= 32; - } - }; - private final UserPreferenceService preferenceService; + @Override + public void onMessageReceived(@NotNull MessageReceivedEvent event) { + handleMessageEvent(event); + autoCodeFormatter.handleMessageEvent(event); + } - @Override - public void onMessageReceived(@NotNull MessageReceivedEvent event) { - // listen for the posts initial message. Why this has to be done is further described in the JDA Discord: - // https://canary.discord.com/channels/125227483518861312/1053705384466059354/1053705384466059354 - if (newThreadChannels.contains(event.getChannel().getIdLong())) { - HelpConfig config = botConfig.get(event.getGuild()).getHelpConfig(); - ThreadChannel post = event.getChannel().asThreadChannel(); - // send post buttons - post.sendMessageComponents(ActionRow.of( - createCloseSuggestionButton(post), - Button.secondary(ComponentIdBuilder.build(HelpManager.HELP_GUIDELINES_IDENTIFIER), "View Help Guidelines") - )).queue(success -> post.sendMessageFormat(config.getReservedChannelMessageTemplate(), UserSnowflake.fromId(post.getOwnerId()).getAsMention(), config.getInactivityTimeoutMinutes()).queue()); - newThreadChannels.remove(event.getChannel().getIdLong()); - return; - } - if (event.getMessage().getAuthor().isSystem() || event.getMessage().getAuthor().isBot()) { - return; - } - // check for forum post - if (isInvalidForumPost(event.getChannel())) { - return; - } - ThreadChannel post = event.getChannel().asThreadChannel(); - if (isInvalidHelpForumChannel(post.getParentChannel().asForumChannel())) { - return; - } - // cache messages - List messages = new ArrayList<>(); - messages.add(event.getMessage()); - if (HELP_POST_MESSAGES.containsKey(post.getIdLong())) { - messages.addAll(HELP_POST_MESSAGES.get(post.getIdLong())); - } - HELP_POST_MESSAGES.put(post.getIdLong(), messages); - // suggest to close post on "problem solved"-messages - replyCloseSuggestionIfPatternMatches(event.getMessage()); - } + private void handleMessageEvent(@NotNull MessageReceivedEvent event) { + // listen for the posts initial message. Why this has to be done is further described in the JDA Discord: + // https://canary.discord.com/channels/125227483518861312/1053705384466059354/1053705384466059354 + if (newThreadChannels.contains(event.getChannel().getIdLong())) { + HelpConfig config = botConfig.get(event.getGuild()).getHelpConfig(); + ThreadChannel post = event.getChannel().asThreadChannel(); + // send post buttons + post.sendMessageComponents(ActionRow.of( + createCloseSuggestionButton(post), + Button.secondary(ComponentIdBuilder.build(HelpManager.HELP_GUIDELINES_IDENTIFIER), "View Help Guidelines") + )).submit(); + post.sendMessageFormat(config.getReservedChannelMessageTemplate(), UserSnowflake.fromId(post.getOwnerId()).getAsMention(), config.getInactivityTimeoutMinutes()).submit(); + newThreadChannels.remove(event.getChannel().getIdLong()); + return; + } + if (event.getMessage().getAuthor().isSystem() || event.getMessage().getAuthor().isBot()) { + return; + } + // check for forum post + if (isInvalidForumPost(event.getChannel())) { + return; + } + ThreadChannel post = event.getChannel().asThreadChannel(); + if (isInvalidHelpForumChannel(post.getParentChannel().asForumChannel())) { + return; + } + // cache messages + List messages = new ArrayList<>(); + messages.add(event.getMessage()); + if (HELP_POST_MESSAGES.containsKey(post.getIdLong())) { + messages.addAll(HELP_POST_MESSAGES.get(post.getIdLong())); + } + HELP_POST_MESSAGES.put(post.getIdLong(), messages); + // suggest to close post on "problem solved"-messages + replyCloseSuggestionIfPatternMatches(event.getMessage()); + } - private void replyCloseSuggestionIfPatternMatches(Message msg) { - String content = msg.getContentRaw().toLowerCase(); - if (content.contains("```")) { - return; - } - long postId = msg.getChannel().getIdLong(); - if (recentlyCloseSuggestedPosts.containsKey(postId) && recentlyCloseSuggestedPosts.get(postId) > System.currentTimeMillis()) { - return; - } - if(msg.getChannel().asThreadChannel().getOwnerIdLong() == msg.getAuthor().getIdLong()) { - for (String[] detector : closeSuggestionDetectors) { - if (doesMatchDetector(content, detector)) { - msg.reply(""" - If you are finished with your post, please close it. - If you are not, please ignore this message. - Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts. - """) - .addActionRow(createCloseSuggestionButton(msg.getChannel().asThreadChannel()), - Button.secondary(InteractionUtils.DELETE_ORIGINAL_TEMPLATE, "\uD83D\uDDD1️")) - .queue(); - recentlyCloseSuggestedPosts.put(postId, System.currentTimeMillis() + SUGGEST_CLOSE_TIMEOUT); - } - } - } - } + private void replyCloseSuggestionIfPatternMatches(Message msg) { + String content = msg.getContentRaw().toLowerCase(); + if (content.contains("```")) { + return; + } + long postId = msg.getChannel().getIdLong(); + if (recentlyCloseSuggestedPosts.containsKey(postId) && recentlyCloseSuggestedPosts.get(postId) > System.currentTimeMillis()) { + return; + } + if (msg.getChannel().asThreadChannel().getOwnerIdLong() == msg.getAuthor().getIdLong()) { + for (String[] detector : closeSuggestionDetectors) { + if (doesMatchDetector(content, detector)) { + msg.reply(""" + If you are finished with your post, please close it. + If you are not, please ignore this message. + Note that you will not be able to send further messages here after this post have been closed but you will be able to create new posts. + """) + .addActionRow(createCloseSuggestionButton(msg.getChannel().asThreadChannel()), + Button.secondary(InteractionUtils.DELETE_ORIGINAL_TEMPLATE, "\uD83D\uDDD1️")) + .queue(); + recentlyCloseSuggestedPosts.put(postId, System.currentTimeMillis() + SUGGEST_CLOSE_TIMEOUT); + } + } + } + } - private boolean doesMatchDetector(String content, String[] detector) { - int currentIndex = 0; - for (String keyword : detector) { - currentIndex = content.indexOf(keyword); - if (currentIndex == -1) { - return false; - } - } - return true; - } + private boolean doesMatchDetector(String content, String[] detector) { + int currentIndex = 0; + for (String keyword : detector) { + currentIndex = content.indexOf(keyword); + if (currentIndex == -1) { + return false; + } + } + return true; + } - private Button createCloseSuggestionButton(ThreadChannel post) { - return Button.primary(ComponentIdBuilder.build(HelpManager.HELP_CLOSE_IDENTIFIER, post.getIdLong()), "Close Post"); - } + private Button createCloseSuggestionButton(ThreadChannel post) { + return Button.primary(ComponentIdBuilder.build(HelpManager.HELP_CLOSE_IDENTIFIER, post.getIdLong()), "Close Post"); + } - @Override - public void onChannelCreate(@NotNull ChannelCreateEvent event) { - if (isInvalidForumPost(event.getChannel())) { - return; - } - ThreadChannel post = event.getChannel().asThreadChannel(); - if (isInvalidHelpForumChannel(post.getParentChannel().asForumChannel())) { - return; - } - // add thread id to a temporary cache to avoid potential missing author message - // more info on why this has to be done: https://canary.discord.com/channels/125227483518861312/1053705384466059354/1053705384466059354 (JDA Discord) - newThreadChannels.add(event.getChannel().getIdLong()); - } + @Override + public void onChannelCreate(@NotNull ChannelCreateEvent event) { + if (isInvalidForumPost(event.getChannel())) { + return; + } + ThreadChannel post = event.getChannel().asThreadChannel(); + if (isInvalidHelpForumChannel(post.getParentChannel().asForumChannel())) { + return; + } + // add thread id to a temporary cache to avoid potential missing author message + // more info on why this has to be done: https://canary.discord.com/channels/125227483518861312/1053705384466059354/1053705384466059354 (JDA Discord) + newThreadChannels.add(event.getChannel().getIdLong()); + } - @Override - public void handleButton(@NotNull ButtonInteractionEvent event, @NotNull Button button) { - String[] id = ComponentIdBuilder.split(event.getComponentId()); - if (isInvalidForumPost(event.getChannel()) || - isInvalidHelpForumChannel(event.getChannel().asThreadChannel().getParentChannel().asForumChannel()) - ) { - Responses.error(event, "This button may only be used inside help forum threads.").queue(); - return; - } - ThreadChannel post = event.getChannel().asThreadChannel(); - HelpManager manager = new HelpManager(post, dbActions, botConfig, helpAccountRepository, helpTransactionRepository, preferenceService); - switch (id[0]) { - case HelpManager.HELP_THANKS_IDENTIFIER -> handleHelpThanksInteraction(event, manager, id); - case HelpManager.HELP_GUIDELINES_IDENTIFIER -> - handleReplyGuidelines(event, post.getParentChannel().asForumChannel()); - case HelpManager.HELP_CLOSE_IDENTIFIER -> handlePostClose(event, manager); - default -> event.reply("Unknown interaction.").queue(); - } - } + @Override + public void handleButton(@NotNull ButtonInteractionEvent event, @NotNull Button button) { + String[] id = ComponentIdBuilder.split(event.getComponentId()); + if (isInvalidForumPost(event.getChannel()) || + isInvalidHelpForumChannel(event.getChannel().asThreadChannel().getParentChannel().asForumChannel()) + ) { + Responses.error(event, "This button may only be used inside help forum threads.").queue(); + return; + } + ThreadChannel post = event.getChannel().asThreadChannel(); + HelpManager manager = new HelpManager(post, dbActions, botConfig, helpAccountRepository, helpTransactionRepository, preferenceService); + switch (id[0]) { + case HelpManager.HELP_THANKS_IDENTIFIER -> handleHelpThanksInteraction(event, manager, id); + case HelpManager.HELP_GUIDELINES_IDENTIFIER -> + handleReplyGuidelines(event, post.getParentChannel().asForumChannel()); + case HelpManager.HELP_CLOSE_IDENTIFIER -> handlePostClose(event, manager); + default -> event.reply("Unknown interaction.").queue(); + } + } - private boolean isInvalidForumPost(@NotNull Channel channel) { - return channel.getType() != ChannelType.GUILD_PUBLIC_THREAD || - ((ThreadChannel) channel).getParentChannel().getType() != ChannelType.FORUM; - } + private boolean isInvalidForumPost(@NotNull Channel channel) { + return channel.getType() != ChannelType.GUILD_PUBLIC_THREAD || + ((ThreadChannel) channel).getParentChannel().getType() != ChannelType.FORUM; + } - private boolean isInvalidHelpForumChannel(@NotNull ForumChannel forum) { - HelpConfig config = botConfig.get(forum.getGuild()).getHelpConfig(); - return config.getHelpForumChannelId() != forum.getIdLong(); - } + private boolean isInvalidHelpForumChannel(@NotNull ForumChannel forum) { + HelpConfig config = botConfig.get(forum.getGuild()).getHelpConfig(); + return config.getHelpForumChannelId() != forum.getIdLong(); + } - private void handleHelpThanksInteraction(@NotNull ButtonInteractionEvent event, @NotNull HelpManager manager, String @NotNull [] id) { - ThreadChannel post = manager.getPostThread(); - HelpConfig config = botConfig.get(event.getGuild()).getHelpConfig(); - if (event.getUser().getIdLong() != post.getOwnerIdLong()) { - Responses.warning(event, "Sorry, only the person who reserved this channel can thank users.").queue(); - return; - } - switch (id[2]) { - case "done" -> handleThanksCloseButton(event, manager, post, config); - case "cancel" -> event.getMessage().delete().queue(); - default -> { - List