From 34608cb930794599e7265592fb4ee7a44bbf10b5 Mon Sep 17 00:00:00 2001 From: Zabuzard Date: Thu, 7 Jul 2022 08:48:34 +0200 Subject: [PATCH 1/6] Bugfix with temp actions being revoked too early --- .../commands/moderation/temp/TemporaryModerationRoutine.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/moderation/temp/TemporaryModerationRoutine.java b/application/src/main/java/org/togetherjava/tjbot/commands/moderation/temp/TemporaryModerationRoutine.java index 26671c196e..ab4bd88a67 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/moderation/temp/TemporaryModerationRoutine.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/moderation/temp/TemporaryModerationRoutine.java @@ -81,13 +81,14 @@ private void checkExpiredActions() { private void processGroupedActions(@NotNull RevocationGroupIdentifier groupIdentifier) { // Do not revoke an action which was overwritten by a permanent action that was issued - // afterwards + // afterwards, or if the last action has not even expired yet // For example if a user was perm-banned after being temp-banned ActionRecord lastApplyAction = actionsStore .findLastActionAgainstTargetByType(groupIdentifier.guildId, groupIdentifier.targetId, groupIdentifier.type) .orElseThrow(); - if (lastApplyAction.actionExpiresAt() == null) { + if (lastApplyAction.actionExpiresAt() == null + || lastApplyAction.actionExpiresAt().isAfter(Instant.now())) { return; } From fd3681cbac2ed1e32af3f8b0b4839b21b1ed9eeb Mon Sep 17 00:00:00 2001 From: Zabuzard Date: Thu, 7 Jul 2022 12:54:44 +0200 Subject: [PATCH 2/6] Introducing "isEffective" helper, minor refactoring for readability --- .../tjbot/commands/moderation/ActionRecord.java | 12 +++++++++++- .../moderation/RejoinModerationRoleListener.java | 10 ++-------- .../moderation/temp/TemporaryModerationRoutine.java | 11 ++++------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/moderation/ActionRecord.java b/application/src/main/java/org/togetherjava/tjbot/commands/moderation/ActionRecord.java index b568a2e0d2..910b7c8663 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/moderation/ActionRecord.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/moderation/ActionRecord.java @@ -26,7 +26,7 @@ public record ActionRecord(int caseId, @NotNull Instant issuedAt, long guildId, /** * Creates the action record that corresponds to the given action entry from the database table. - * + * * @param action the action to convert * @return the corresponding action record */ @@ -37,4 +37,14 @@ public record ActionRecord(int caseId, @NotNull Instant issuedAt, long guildId, ModerationAction.valueOf(action.getActionType()), action.getActionExpiresAt(), action.getReason()); } + + /** + * Whether this action is still effective. That is, it is either a permanent action or temporary + * but not expired yet. + * + * @return True when still effective, false otherwise + */ + public boolean isEffective() { + return actionExpiresAt == null || actionExpiresAt.isAfter(Instant.now()); + } } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/moderation/RejoinModerationRoleListener.java b/application/src/main/java/org/togetherjava/tjbot/commands/moderation/RejoinModerationRoleListener.java index d2822c65f5..2d306f0247 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/moderation/RejoinModerationRoleListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/moderation/RejoinModerationRoleListener.java @@ -12,7 +12,6 @@ import org.togetherjava.tjbot.commands.EventReceiver; import org.togetherjava.tjbot.config.Config; -import java.time.Instant; import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -82,23 +81,18 @@ private boolean shouldApplyModerationRole(@NotNull ModerationRole moderationRole member.getGuild().getIdLong(), member.getIdLong(), moderationRole.revokeAction); if (lastRevokeAction.isEmpty()) { // User was never e.g. unmuted - return isActionEffective(lastApplyAction.orElseThrow()); + return lastApplyAction.orElseThrow().isEffective(); } // The last issued action takes priority if (lastApplyAction.orElseThrow() .issuedAt() .isAfter(lastRevokeAction.orElseThrow().issuedAt())) { - return isActionEffective(lastApplyAction.orElseThrow()); + return lastApplyAction.orElseThrow().isEffective(); } return false; } - private static boolean isActionEffective(@NotNull ActionRecord action) { - // Effective if permanent or expires in the future - return action.actionExpiresAt() == null || action.actionExpiresAt().isAfter(Instant.now()); - } - private static void applyModerationRole(@NotNull ModerationRole moderationRole, @NotNull Member member) { Guild guild = member.getGuild(); diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/moderation/temp/TemporaryModerationRoutine.java b/application/src/main/java/org/togetherjava/tjbot/commands/moderation/temp/TemporaryModerationRoutine.java index ab4bd88a67..78e393e3b0 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/moderation/temp/TemporaryModerationRoutine.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/moderation/temp/TemporaryModerationRoutine.java @@ -13,7 +13,6 @@ import org.togetherjava.tjbot.commands.moderation.ModerationActionsStore; import org.togetherjava.tjbot.config.Config; -import java.time.Instant; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -80,15 +79,14 @@ private void checkExpiredActions() { } private void processGroupedActions(@NotNull RevocationGroupIdentifier groupIdentifier) { - // Do not revoke an action which was overwritten by a permanent action that was issued - // afterwards, or if the last action has not even expired yet + // Do not revoke an action which was overwritten by a still effective action that was issued + // afterwards // For example if a user was perm-banned after being temp-banned ActionRecord lastApplyAction = actionsStore .findLastActionAgainstTargetByType(groupIdentifier.guildId, groupIdentifier.targetId, groupIdentifier.type) .orElseThrow(); - if (lastApplyAction.actionExpiresAt() == null - || lastApplyAction.actionExpiresAt().isAfter(Instant.now())) { + if (lastApplyAction.isEffective()) { return; } @@ -102,8 +100,7 @@ private void processGroupedActions(@NotNull RevocationGroupIdentifier groupIdent if (lastRevokeActionOpt.isPresent()) { ActionRecord lastRevokeAction = lastRevokeActionOpt.orElseThrow(); if (lastRevokeAction.issuedAt().isAfter(lastApplyAction.issuedAt()) - && (lastRevokeAction.actionExpiresAt() == null - || lastRevokeAction.actionExpiresAt().isAfter(Instant.now()))) { + && lastRevokeAction.isEffective()) { return; } } From 4d10b425705d8358606fb05ad16706b5dc4acf91 Mon Sep 17 00:00:00 2001 From: Daniel Tischner Date: Mon, 18 Jul 2022 09:07:37 +0200 Subject: [PATCH 3/6] Bugfix with /change_help_title allowing too long titles (#466) * added title verification to /change_help_title * also decreased title max length to 70 to account for long categories like "[Together Java Bot]" * Minor improvement (CR Tanish) --- .../commands/help/ChangeHelpTitleCommand.java | 20 ++++++++++++++++++- .../tjbot/commands/help/HelpSystemHelper.java | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpTitleCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpTitleCommand.java index a94471c103..14a6d0cb5b 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpTitleCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/ChangeHelpTitleCommand.java @@ -4,6 +4,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; import net.dv8tion.jda.api.entities.ThreadChannel; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback; import net.dv8tion.jda.api.interactions.commands.OptionType; import org.jetbrains.annotations.NotNull; import org.togetherjava.tjbot.commands.SlashCommandAdapter; @@ -15,6 +16,9 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; +import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MAX; +import static org.togetherjava.tjbot.commands.help.HelpSystemHelper.TITLE_COMPACT_LENGTH_MIN; + /** * Implements the {@code /change-help-title} command, which is able to change the title of a help * thread. @@ -54,7 +58,7 @@ public ChangeHelpTitleCommand(@NotNull HelpSystemHelper helper) { public void onSlashCommand(@NotNull SlashCommandInteractionEvent event) { String title = event.getOption(TITLE_OPTION).getAsString(); - if (!helper.handleIsHelpThread(event)) { + if (!helper.handleIsHelpThread(event) || !handleIsValidTitle(title, event)) { return; } @@ -88,4 +92,18 @@ private boolean isHelpThreadOnCooldown(@NotNull ThreadChannel helpThread) { .filter(Instant.now()::isBefore) .isPresent(); } + + private boolean handleIsValidTitle(@NotNull CharSequence title, @NotNull IReplyCallback event) { + if (HelpSystemHelper.isTitleValid(title)) { + return true; + } + + event.reply( + "Sorry, but the title length (after removal of special characters) has to be between %d and %d." + .formatted(TITLE_COMPACT_LENGTH_MIN, TITLE_COMPACT_LENGTH_MAX)) + .setEphemeral(true) + .queue(); + + return false; + } } diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpSystemHelper.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpSystemHelper.java index b7f5e77f1a..c6bbd74544 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpSystemHelper.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/HelpSystemHelper.java @@ -36,7 +36,7 @@ public final class HelpSystemHelper { private static final Pattern TITLE_COMPACT_REMOVAL_PATTERN = Pattern.compile("\\W"); static final int TITLE_COMPACT_LENGTH_MIN = 2; - static final int TITLE_COMPACT_LENGTH_MAX = 80; + static final int TITLE_COMPACT_LENGTH_MAX = 70; private final Predicate isOverviewChannelName; private final String overviewChannelPattern; From ea59cf13935a6938d09c112107ebc1962a9a27df Mon Sep 17 00:00:00 2001 From: Daniel Tischner Date: Mon, 18 Jul 2022 09:08:27 +0200 Subject: [PATCH 4/6] Defered thread creation to avoid crash when hitting rate limits (#465) --- .../tjbot/commands/help/AskCommand.java | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/help/AskCommand.java b/application/src/main/java/org/togetherjava/tjbot/commands/help/AskCommand.java index 3d9f72270b..63f0b23951 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/help/AskCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/help/AskCommand.java @@ -3,12 +3,12 @@ import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.exceptions.ErrorResponseException; +import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.requests.ErrorResponse; import net.dv8tion.jda.api.requests.RestAction; -import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -92,11 +92,16 @@ public void onSlashCommand(@NotNull SlashCommandInteractionEvent event) { } TextChannel overviewChannel = maybeOverviewChannel.orElseThrow(); + InteractionHook eventHook = event.getHook(); + Member author = event.getMember(); + Guild guild = event.getGuild(); + event.deferReply(true).queue(); + overviewChannel.createThreadChannel("[%s] %s".formatted(category, title)) - .flatMap(threadChannel -> handleEvent(event, threadChannel, event.getMember(), title, - category)) + .flatMap(threadChannel -> handleEvent(eventHook, threadChannel, author, title, category, + guild)) .queue(any -> { - }, e -> handleFailure(e, event)); + }, e -> handleFailure(e, eventHook)); } private boolean handleIsStagingChannel(@NotNull IReplyCallback event) { @@ -125,11 +130,11 @@ private boolean handleIsValidTitle(@NotNull CharSequence title, @NotNull IReplyC return false; } - private @NotNull RestAction handleEvent(@NotNull IReplyCallback event, + private @NotNull RestAction handleEvent(@NotNull InteractionHook eventHook, @NotNull ThreadChannel threadChannel, @NotNull Member author, @NotNull String title, - @NotNull String category) { - return sendInitialMessage(event.getGuild(), threadChannel, author, title, category) - .flatMap(any -> notifyUser(event, threadChannel)) + @NotNull String category, @NotNull Guild guild) { + return sendInitialMessage(guild, threadChannel, author, title, category) + .flatMap(any -> notifyUser(eventHook, threadChannel)) .flatMap(any -> helper.sendExplanationMessage(threadChannel)); } @@ -153,22 +158,21 @@ private RestAction sendInitialMessage(@NotNull Guild guild, .flatMap(message -> message.editMessage(contentWithRole)); } - private static @NotNull ReplyCallbackAction notifyUser(@NotNull IReplyCallback event, + private static @NotNull RestAction notifyUser(@NotNull InteractionHook eventHook, @NotNull IMentionable threadChannel) { - return event.reply(""" + return eventHook.editOriginal(""" Created a thread for you: %s - Please ask your question there, thanks.""".formatted(threadChannel.getAsMention())) - .setEphemeral(true); + Please ask your question there, thanks.""".formatted(threadChannel.getAsMention())); } - private static void handleFailure(@NotNull Throwable exception, @NotNull IReplyCallback event) { + private static void handleFailure(@NotNull Throwable exception, + @NotNull InteractionHook eventHook) { if (exception instanceof ErrorResponseException responseException) { ErrorResponse response = responseException.getErrorResponse(); if (response == ErrorResponse.MAX_CHANNELS || response == ErrorResponse.MAX_ACTIVE_THREADS) { - event.reply( + eventHook.editOriginal( "It seems that there are currently too many active questions, please try again in a few minutes.") - .setEphemeral(true) .queue(); return; } From 9305cd08066678544973514ab827d07456c06684 Mon Sep 17 00:00:00 2001 From: Daniel Tischner Date: Mon, 18 Jul 2022 09:09:49 +0200 Subject: [PATCH 5/6] Upgrade to Java 18 (#467) Gradle has no official support for it yet, we use the latest release candidate instead (7.5 rc 5) --- .github/workflows/basic-checks.yml | 2 +- .github/workflows/code-analysis.yml | 2 +- .github/workflows/docker-publish.yaml | 2 +- .github/workflows/docker-verify.yaml | 2 +- .github/workflows/releases.yaml | 2 +- README.md | 4 ++-- application/build.gradle | 2 +- build.gradle | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/basic-checks.yml b/.github/workflows/basic-checks.yml index 494cd7a47e..12e467306c 100644 --- a/.github/workflows/basic-checks.yml +++ b/.github/workflows/basic-checks.yml @@ -3,7 +3,7 @@ name: Basic checks on: [pull_request] env: - JAVA_VERSION: 17 + JAVA_VERSION: 18 jobs: spotless: diff --git a/.github/workflows/code-analysis.yml b/.github/workflows/code-analysis.yml index a56a4e5d29..e10ae6db1c 100644 --- a/.github/workflows/code-analysis.yml +++ b/.github/workflows/code-analysis.yml @@ -8,7 +8,7 @@ on: - cron: '0 20 * * 4' env: - JAVA_VERSION: 17 + JAVA_VERSION: 18 jobs: sonar: diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index 6f536c82c9..b6d354a175 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -7,7 +7,7 @@ on: - 'master' env: - JAVA_VERSION: 17 + JAVA_VERSION: 18 jobs: docker: diff --git a/.github/workflows/docker-verify.yaml b/.github/workflows/docker-verify.yaml index a07bb8a9e9..be8e970de9 100644 --- a/.github/workflows/docker-verify.yaml +++ b/.github/workflows/docker-verify.yaml @@ -3,7 +3,7 @@ name: Docker Verify on: [pull_request] env: - JAVA_VERSION: 17 + JAVA_VERSION: 18 jobs: docker: diff --git a/.github/workflows/releases.yaml b/.github/workflows/releases.yaml index 69edb9ecc9..7b44328b5d 100644 --- a/.github/workflows/releases.yaml +++ b/.github/workflows/releases.yaml @@ -10,7 +10,7 @@ defaults: shell: bash env: - JAVA_VERSION: 17 + JAVA_VERSION: 18 jobs: diff --git a/README.md b/README.md index f0982139ea..cebc069fa1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # TJ-Bot [![codefactor](https://img.shields.io/codefactor/grade/github/together-java/tj-bot)](https://www.codefactor.io/repository/github/together-java/tj-bot) -![Java](https://img.shields.io/badge/Java-17%2B-ff696c) +![Java](https://img.shields.io/badge/Java-18-ff696c) [![license](https://img.shields.io/github/license/Together-Java/TJ-Bot)](https://github.com/Together-Java/TJ-Bot/blob/master/LICENSE) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/Together-Java/TJ-Bot?label=release) @@ -28,4 +28,4 @@ Head over to the [Wiki](https://github.com/Together-Java/TJ-Bot/wiki) as general Jar downloads are available from the [release section](https://github.com/Together-Java/TJ-Bot/releases). -Alternatively, you can also download the project using your favorite build tool. Artifacts are made available via https://jitpack.io. Our `groupId` is `com.github.Together-Java`, the `artifactId` is `TJ-Bot`. \ No newline at end of file +Alternatively, you can also download the project using your favorite build tool. Artifacts are made available via https://jitpack.io. Our `groupId` is `com.github.Together-Java`, the `artifactId` is `TJ-Bot`. diff --git a/application/build.gradle b/application/build.gradle index c448842157..e080e1b7d7 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -18,7 +18,7 @@ repositories { var outputImage = 'togetherjava.duckdns.org:5001/togetherjava/tjbot:' + System.getenv('BRANCH_NAME') ?: 'latest' jib { - from.image = 'eclipse-temurin:17' + from.image = 'eclipse-temurin:18' to { image = outputImage auth { diff --git a/build.gradle b/build.gradle index b42322463b..46b1ad685f 100644 --- a/build.gradle +++ b/build.gradle @@ -63,8 +63,8 @@ subprojects { options.encoding = 'UTF-8' // Nails the Java-Version of every Subproject - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_18 + targetCompatibility = JavaVersion.VERSION_18 } compileJava(compileTasks) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102e09..8396279267 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-rc-5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 454f1aa3b4704f7e70adf9641cbc2672c158e61b Mon Sep 17 00:00:00 2001 From: Taz <73871477+Tanish-Azad@users.noreply.github.com> Date: Thu, 21 Jul 2022 18:11:17 +0530 Subject: [PATCH 6/6] Auto create threads for suggestions (#469) * Auto create threads for suggestions * added a condition to general checks * made a method for thread creation * added notnull annotation to message argument in createThread method * removed unnecessary checks * changed algorithm to generate thread titles * removed unnecessary variable --- .../commands/basic/SuggestionsUpDownVoter.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/application/src/main/java/org/togetherjava/tjbot/commands/basic/SuggestionsUpDownVoter.java b/application/src/main/java/org/togetherjava/tjbot/commands/basic/SuggestionsUpDownVoter.java index e3d7b5b29a..280f32599e 100644 --- a/application/src/main/java/org/togetherjava/tjbot/commands/basic/SuggestionsUpDownVoter.java +++ b/application/src/main/java/org/togetherjava/tjbot/commands/basic/SuggestionsUpDownVoter.java @@ -22,6 +22,7 @@ */ public final class SuggestionsUpDownVoter extends MessageReceiverAdapter { private static final Logger logger = LoggerFactory.getLogger(SuggestionsUpDownVoter.class); + private static final int TITLE_MAX_LENGTH = 60; private static final String FALLBACK_UP_VOTE = "👍"; private static final String FALLBACK_DOWN_VOTE = "👎"; @@ -47,10 +48,27 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) { Guild guild = event.getGuild(); Message message = event.getMessage(); + createThread(message); reactWith(config.getUpVoteEmoteName(), FALLBACK_UP_VOTE, guild, message); reactWith(config.getDownVoteEmoteName(), FALLBACK_DOWN_VOTE, guild, message); } + private static void createThread(@NotNull Message message) { + String title = message.getContentRaw(); + + if (title.length() >= TITLE_MAX_LENGTH) { + int lastWordEnd = title.lastIndexOf(' ', TITLE_MAX_LENGTH); + + if (lastWordEnd == -1) { + lastWordEnd = TITLE_MAX_LENGTH; + } + + title = title.substring(0, lastWordEnd); + } + + message.createThreadChannel(title).queue(); + } + private static void reactWith(@NotNull String emoteName, @NotNull String fallbackUnicodeEmote, @NotNull Guild guild, @NotNull Message message) { getEmoteByName(emoteName, guild).map(message::addReaction).orElseGet(() -> {