Skip to content
3 changes: 2 additions & 1 deletion application/config.json.template
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,6 @@
],
"special": [
]
}
},
"selectRolesChannelPattern": "select-your-roles"
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public final class Config {
private final JShellConfig jshell;
private final HelperPruneConfig helperPruneConfig;
private final FeatureBlacklistConfig featureBlacklistConfig;
private final String selectRolesChannelPatten;

@SuppressWarnings("ConstructorWithTooManyParameters")
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
Expand Down Expand Up @@ -82,7 +83,9 @@ private Config(@JsonProperty(value = "token", required = true) String token,
@JsonProperty(value = "helperPruneConfig",
required = true) HelperPruneConfig helperPruneConfig,
@JsonProperty(value = "featureBlacklist",
required = true) FeatureBlacklistConfig featureBlacklistConfig) {
required = true) FeatureBlacklistConfig featureBlacklistConfig,
@JsonProperty(value = "selectRolesChannelPattern",
required = true) String selectRolesChannelPatten) {
this.token = Objects.requireNonNull(token);
this.gistApiKey = Objects.requireNonNull(gistApiKey);
this.databasePath = Objects.requireNonNull(databasePath);
Expand Down Expand Up @@ -110,6 +113,7 @@ private Config(@JsonProperty(value = "token", required = true) String token,
this.jshell = Objects.requireNonNull(jshell);
this.helperPruneConfig = Objects.requireNonNull(helperPruneConfig);
this.featureBlacklistConfig = Objects.requireNonNull(featureBlacklistConfig);
this.selectRolesChannelPatten = Objects.requireNonNull(selectRolesChannelPatten);
}

/**
Expand Down Expand Up @@ -368,4 +372,8 @@ public HelperPruneConfig getHelperPruneConfig() {
public FeatureBlacklistConfig getFeatureBlacklistConfig() {
return featureBlacklistConfig;
}

public String getSelectRolesChannelPatten() {
return selectRolesChannelPatten;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
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.concrete.ForumChannel;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -14,11 +14,15 @@
import org.togetherjava.tjbot.features.Routine;
import org.togetherjava.tjbot.features.moderation.audit.ModAuditLogWriter;

import javax.annotation.Nullable;

import java.time.Duration;
import java.time.Instant;
import java.time.Period;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.regex.Pattern;

import static org.togetherjava.tjbot.db.generated.tables.HelpChannelMessages.HELP_CHANNEL_MESSAGES;

Expand All @@ -42,6 +46,7 @@ public final class AutoPruneHelperRoutine implements Routine {
private final ModAuditLogWriter modAuditLogWriter;
private final Database database;
private final List<String> allCategories;
private final Predicate<String> selectYourRolesChannelNamePredicate;

/**
* Creates a new instance.
Expand All @@ -64,6 +69,8 @@ public AutoPruneHelperRoutine(Config config, HelpSystemHelper helper,
pruneMemberAmount = helperPruneConfig.pruneMemberAmount();
inactiveAfter = Period.ofDays(helperPruneConfig.inactivateAfterDays());
recentlyJoinedDays = helperPruneConfig.recentlyJoinedDays();
selectYourRolesChannelNamePredicate =
Pattern.compile(config.getSelectRolesChannelPatten()).asMatchPredicate();
}

@Override
Expand All @@ -77,25 +84,21 @@ public void runRoutine(JDA jda) {
}

private void pruneForGuild(Guild guild) {
ForumChannel helpForum = guild.getForumChannels()
.stream()
.filter(channel -> helper.isHelpForumName(channel.getName()))
.findAny()
.orElseThrow();
Instant now = Instant.now();
TextChannel selectRoleChannel = getSelectRolesChannelOptional(guild.getJDA()).orElse(null);

allCategories.stream()
.map(category -> helper.handleFindRoleForCategory(category, guild))
.filter(Optional::isPresent)
.map(Optional::orElseThrow)
.forEach(role -> pruneRoleIfFull(role, helpForum, now));
.forEach(role -> pruneRoleIfFull(role, selectRoleChannel, now));
}

private void pruneRoleIfFull(Role role, ForumChannel helpForum, Instant when) {
private void pruneRoleIfFull(Role role, @Nullable TextChannel selectRoleChannel, Instant when) {
role.getGuild().findMembersWithRoles(role).onSuccess(members -> {
if (isRoleFull(members)) {
logger.debug("Helper role {} is full, starting to prune.", role.getName());
pruneRole(role, members, helpForum, when);
pruneRole(role, members, selectRoleChannel, when);
}
});
}
Expand All @@ -104,8 +107,8 @@ private boolean isRoleFull(Collection<?> members) {
return members.size() >= roleFullThreshold;
}

private void pruneRole(Role role, List<? extends Member> members, ForumChannel helpForum,
Instant when) {
private void pruneRole(Role role, List<? extends Member> members,
@Nullable TextChannel selectRoleChannel, Instant when) {
List<Member> membersShuffled = new ArrayList<>(members);
Collections.shuffle(membersShuffled);

Expand All @@ -128,7 +131,7 @@ private void pruneRole(Role role, List<? extends Member> members, ForumChannel h

logger.info("Pruning {} users {} from role {}", membersToPrune.size(), membersToPrune,
role.getName());
membersToPrune.forEach(member -> pruneMemberFromRole(member, role, helpForum));
membersToPrune.forEach(member -> pruneMemberFromRole(member, role, selectRoleChannel));
}

private boolean isMemberInactive(Member member, Instant when) {
Expand All @@ -150,15 +153,20 @@ private boolean isMemberInactive(Member member, Instant when) {
.and(HELP_CHANNEL_MESSAGES.SENT_AT.greaterThan(latestActiveMoment)))) == 0;
}

private void pruneMemberFromRole(Member member, Role role, ForumChannel helpForum) {
private void pruneMemberFromRole(Member member, Role role,
@Nullable TextChannel selectRoleChannel) {
Guild guild = member.getGuild();

String channelMentionOrFallbackMessage =
selectRoleChannel == null ? "role selection channel"
: selectRoleChannel.getAsMention();

String dmMessage =
"""
You seem to have been inactive for some time in server **%s**, hence we removed you from the **%s** role.
If that was a mistake, just head back to %s and select the role again.
Sorry for any inconvenience caused by this 🙇"""
.formatted(guild.getName(), role.getName(), helpForum.getAsMention());
.formatted(guild.getName(), role.getName(), channelMentionOrFallbackMessage);

guild.removeRoleFromMember(member, role)
.flatMap(any -> member.getUser().openPrivateChannel())
Expand All @@ -173,4 +181,11 @@ private void warnModsAbout(String message, Guild guild) {

modAuditLogWriter.write("Auto-prune helpers", message, null, Instant.now(), guild);
}

private Optional<TextChannel> getSelectRolesChannelOptional(JDA jda) {
return jda.getTextChannels()
.stream()
.filter(textChannel -> selectYourRolesChannelNamePredicate.test(textChannel.getName()))
.findFirst();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,9 @@ public void onModalSubmitted(ModalInteractionEvent event, List<String> args) {
}

private boolean isPostAuthor(Member interactionUser, Message message) {
if (message.getEmbeds().isEmpty())
if (message.getEmbeds().isEmpty()) {
return false;
}

String embedAuthor = Objects
.requireNonNull(message.getEmbeds().get(0).getAuthor(),
Expand Down