diff --git a/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java b/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java index c2cdb7b2ad..5f9ae9a4bb 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java @@ -43,8 +43,14 @@ public final class GitHubReference extends MessageReceiverAdapter { */ static final Pattern ISSUE_REFERENCE_PATTERN = Pattern.compile("#(?<%s>\\d{1,5})".formatted(ID_GROUP)); - private static final int ISSUE_OPEN = Color.green.getRGB(); - private static final int ISSUE_CLOSE = Color.red.getRGB(); + + // Representing different GitHub states of an Issue/PR + private static final Color OPEN_STATE = Color.green; + private static final Color CLOSE_STATE = Color.red; + private static final Color MERGED_STATE = new Color(141, 106, 187); + private static final Color NOT_PLANNED_STATE = new Color(72, 72, 72); + private static final Color DRAFT_STATE = Color.gray; + /** * A constant representing the date and time formatter used for formatting the creation date of @@ -167,9 +173,7 @@ MessageEmbed generateReply(GHIssue issue) throws UncheckedIOException { String dateOfCreation = FORMATTER.format(createdAt); String footer = "%s • %s • %s".formatted(labels, assignees, dateOfCreation); - - return new EmbedBuilder() - .setColor(issue.getState() == GHIssueState.OPEN ? ISSUE_OPEN : ISSUE_CLOSE) + return new EmbedBuilder().setColor(getIssueStateColor(issue)) .setTitle(title, titleUrl) .setDescription(description) .setAuthor(issue.getUser().getName(), null, issue.getUser().getAvatarUrl()) @@ -181,6 +185,26 @@ MessageEmbed generateReply(GHIssue issue) throws UncheckedIOException { } } + /** + * Returns the color based on the state of the issue/PR + */ + private Color getIssueStateColor(GHIssue issue) throws IOException { + if (issue instanceof GHPullRequest pr) { + if (pr.isMerged()) { + return MERGED_STATE; + } else if (pr.isDraft()) { + return DRAFT_STATE; + } + } else { + if (issue.getStateReason() == GHIssueStateReason.COMPLETED) { + return MERGED_STATE; + } else if (issue.getStateReason() == GHIssueStateReason.NOT_PLANNED) { + return NOT_PLANNED_STATE; + } + } + return issue.getState() == GHIssueState.OPEN ? OPEN_STATE : CLOSE_STATE; + } + /** * Either properly gathers the name of a user or throws a UncheckedIOException. */ @@ -199,6 +223,9 @@ Optional findIssue(int id, String targetIssueTitle) { return repositories.stream().map(repository -> { try { GHIssue issue = repository.getIssue(id); + if (issue.isPullRequest()) { + issue = repository.getPullRequest(id); + } if (issue.getTitle().equals(targetIssueTitle)) { return Optional.of(issue); } @@ -216,7 +243,11 @@ Optional findIssue(int id, long defaultRepoId) { .filter(repository -> repository.getId() == defaultRepoId) .map(repository -> { try { - return Optional.of(repository.getIssue(id)); + GHIssue issue = repository.getIssue(id); + if (issue.isPullRequest()) { + issue = repository.getPullRequest(id); + } + return Optional.of(issue); } catch (FileNotFoundException ignored) { return Optional.empty(); } catch (IOException ex) { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java index f048723d09..3994107a6b 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java @@ -7,12 +7,14 @@ 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.Channel; import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.entities.channel.forums.ForumTag; -import net.dv8tion.jda.api.events.channel.ChannelCreateEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.requests.RestAction; +import org.jetbrains.annotations.NotNull; import org.togetherjava.tjbot.features.EventReceiver; import org.togetherjava.tjbot.features.UserInteractionType; @@ -56,20 +58,18 @@ public HelpThreadCreatedListener(HelpSystemHelper helper) { } @Override - public void onChannelCreate(ChannelCreateEvent createEvent) { - if (!createEvent.getChannelType().isThread()) { - return; - } - ThreadChannel threadChannel = createEvent.getChannel().asThreadChannel(); - - if (wasThreadAlreadyHandled(threadChannel.getIdLong())) { - return; - } - - if (!helper.isHelpForumName(threadChannel.getParentChannel().getName())) { - return; + public void onMessageReceived(@NotNull MessageReceivedEvent event) { + if (event.isFromThread()) { + Channel parentChannel = event.getChannel().asThreadChannel().getParentChannel(); + if (helper.isHelpForumName(parentChannel.getName())) { + ThreadChannel threadChannel = event.getChannel().asThreadChannel(); + int messageCount = threadChannel.getMessageCount(); + if (messageCount > 1 || wasThreadAlreadyHandled(threadChannel.getIdLong())) { + return; + } + handleHelpThreadCreated(threadChannel); + } } - handleHelpThreadCreated(threadChannel); } private boolean wasThreadAlreadyHandled(long threadChannelId) { @@ -82,23 +82,10 @@ private boolean wasThreadAlreadyHandled(long threadChannelId) { } private void handleHelpThreadCreated(ThreadChannel threadChannel) { - threadChannel.retrieveMessageById(threadChannel.getIdLong()).queue(message -> { - - long authorId = threadChannel.getOwnerIdLong(); - - if (isPostedBySelfUser(message)) { - // When transfer-command is used - authorId = getMentionedAuthorByMessage(message).getIdLong(); - } - - helper.writeHelpThreadToDatabase(authorId, threadChannel); - }); - - // The creation is delayed, because otherwise it could be too fast and be executed - // after Discord created the thread, but before Discord send OPs initial message. - // Sending messages at that moment is not allowed. - createMessages(threadChannel).and(pinOriginalQuestion(threadChannel)) - .queueAfter(5, TimeUnit.SECONDS); + threadChannel.retrieveStartMessage().flatMap(message -> { + registerThreadDataInDB(message, threadChannel); + return generateAutomatedResponse(threadChannel); + }).flatMap(message -> pinOriginalQuestion(threadChannel)).queue(); } private static User getMentionedAuthorByMessage(Message message) { @@ -126,7 +113,7 @@ private RestAction pinOriginalQuestion(ThreadChannel threadChannel) { return threadChannel.retrieveMessageById(threadChannel.getIdLong()).flatMap(Message::pin); } - private RestAction createMessages(ThreadChannel threadChannel) { + private RestAction generateAutomatedResponse(ThreadChannel threadChannel) { return sendHelperHeadsUp(threadChannel).flatMap(any -> createAIResponse(threadChannel)); } @@ -228,4 +215,15 @@ private void handleDismiss(Member interactionUser, ThreadChannel channel, } deleteMessages.queue(); } + + private void registerThreadDataInDB(Message message, ThreadChannel threadChannel) { + long authorId = threadChannel.getOwnerIdLong(); + + if (isPostedBySelfUser(message)) { + // When transfer-command is used + authorId = getMentionedAuthorByMessage(message).getIdLong(); + } + + helper.writeHelpThreadToDatabase(authorId, threadChannel); + } } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/WhoIsCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/WhoIsCommand.java index 11f95e90cb..137be42f08 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/WhoIsCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/WhoIsCommand.java @@ -3,7 +3,6 @@ import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; -import net.dv8tion.jda.api.interactions.Interaction; import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; @@ -16,7 +15,6 @@ import javax.annotation.CheckReturnValue; import java.awt.*; -import java.time.Instant; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; import java.util.Collection; @@ -77,10 +75,9 @@ private static ReplyCallbackAction handleWhoIsUser(final IReplyCallback event, f + userFlagsToStringItem(user.getFlags()) + "\n**Registration date:** " + DATE_TIME_FORMAT.format(user.getTimeCreated()); - EmbedBuilder embedBuilder = - generateEmbedBuilder(event, user, profile, profile.getAccentColor()).setAuthor( - user.getName(), user.getEffectiveAvatarUrl(), user.getEffectiveAvatarUrl()) - .setDescription(description); + EmbedBuilder embedBuilder = generateEmbedBuilder(user, profile, profile.getAccentColor()) + .setAuthor(user.getName(), user.getEffectiveAvatarUrl(), user.getEffectiveAvatarUrl()) + .setDescription(description); return sendEmbedWithProfileAction(event, embedBuilder.build(), user.getId()); } @@ -100,7 +97,7 @@ private static ReplyCallbackAction handleWhoIsMember(final IReplyCallback event, + DATE_TIME_FORMAT.format(user.getTimeCreated()) + "\n**Roles:** " + formatRoles(member); - EmbedBuilder embedBuilder = generateEmbedBuilder(event, user, profile, effectiveColor) + EmbedBuilder embedBuilder = generateEmbedBuilder(user, profile, effectiveColor) .setAuthor(member.getEffectiveName(), member.getEffectiveAvatarUrl(), member.getEffectiveAvatarUrl()) .setDescription(description); @@ -129,20 +126,15 @@ private static String voiceStateToStringItem(final Member member) { /** * Generates whois embed based on the given parameters. * - * @param event the {@link SlashCommandInteractionEvent} * @param user the {@link User} getting whois'd * @param profile the {@link net.dv8tion.jda.api.entities.User.Profile} of the whois'd user * @param effectiveColor the {@link Color} that the embed will become * @return the generated {@link EmbedBuilder} */ - private static EmbedBuilder generateEmbedBuilder(final Interaction event, final User user, - final User.Profile profile, final Color effectiveColor) { - + private static EmbedBuilder generateEmbedBuilder(final User user, final User.Profile profile, + final Color effectiveColor) { EmbedBuilder embedBuilder = new EmbedBuilder().setThumbnail(user.getEffectiveAvatarUrl()) - .setColor(effectiveColor) - .setFooter("Requested by " + event.getUser().getName(), - event.getMember().getEffectiveAvatarUrl()) - .setTimestamp(Instant.now()); + .setColor(effectiveColor); if (null != profile.getBannerId()) { embedBuilder.setImage(profile.getBannerUrl() + "?size=" + USER_PROFILE_PICTURE_SIZE); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/tags/TagManageCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/tags/TagManageCommand.java index 8d957fedfb..c6b327ecf2 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/tags/TagManageCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/tags/TagManageCommand.java @@ -21,7 +21,6 @@ import javax.annotation.Nullable; import java.nio.charset.StandardCharsets; -import java.time.Instant; import java.time.temporal.TemporalAccessor; import java.util.*; import java.util.function.BiConsumer; @@ -108,8 +107,6 @@ private static void sendSuccessMessage(IReplyCallback event, String id, String a event .replyEmbeds(new EmbedBuilder().setTitle("Success") .setDescription("Successfully %s tag '%s'.".formatted(actionVerb, id)) - .setFooter(event.getUser().getName()) - .setTimestamp(Instant.now()) .setColor(TagSystem.AMBIENT_COLOR) .build()) .queue();