Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/basic-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Basic checks
on: [pull_request]

env:
JAVA_VERSION: 17
JAVA_VERSION: 18

jobs:
spotless:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/code-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
- cron: '0 20 * * 4'

env:
JAVA_VERSION: 17
JAVA_VERSION: 18

jobs:
sonar:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker-publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
- 'master'

env:
JAVA_VERSION: 17
JAVA_VERSION: 18

jobs:
docker:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker-verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Docker Verify
on: [pull_request]

env:
JAVA_VERSION: 17
JAVA_VERSION: 18

jobs:
docker:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/releases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defaults:
shell: bash

env:
JAVA_VERSION: 17
JAVA_VERSION: 18

jobs:

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)

Expand All @@ -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`.
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`.
2 changes: 1 addition & 1 deletion application/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "👎";

Expand All @@ -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(() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -125,11 +130,11 @@ private boolean handleIsValidTitle(@NotNull CharSequence title, @NotNull IReplyC
return false;
}

private @NotNull RestAction<Message> handleEvent(@NotNull IReplyCallback event,
private @NotNull RestAction<Message> 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));
}

Expand All @@ -153,22 +158,21 @@ private RestAction<Message> sendInitialMessage(@NotNull Guild guild,
.flatMap(message -> message.editMessage(contentWithRole));
}

private static @NotNull ReplyCallbackAction notifyUser(@NotNull IReplyCallback event,
private static @NotNull RestAction<Message> 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> isOverviewChannelName;
private final String overviewChannelPattern;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -80,14 +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
// 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) {
if (lastApplyAction.isEffective()) {
return;
}

Expand All @@ -101,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;
}
}
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -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