diff --git a/application/config.json.template b/application/config.json.template index c625b3d212..4819ac8478 100644 --- a/application/config.json.template +++ b/application/config.json.template @@ -107,5 +107,6 @@ ], "special": [ ] - } + }, + "selectRolesChannelPattern": "select-your-roles" } diff --git a/application/src/main/java/org/togetherjava/tjbot/config/Config.java b/application/src/main/java/org/togetherjava/tjbot/config/Config.java index 161bfe75fd..5ab478d9b5 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/Config.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/Config.java @@ -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) @@ -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); @@ -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); } /** @@ -368,4 +372,8 @@ public HelperPruneConfig getHelperPruneConfig() { public FeatureBlacklistConfig getFeatureBlacklistConfig() { return featureBlacklistConfig; } + + public String getSelectRolesChannelPatten() { + return selectRolesChannelPatten; + } } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/AutoPruneHelperRoutine.java b/application/src/main/java/org/togetherjava/tjbot/features/help/AutoPruneHelperRoutine.java index 63563ccf1e..b093fbde4e 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/AutoPruneHelperRoutine.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/AutoPruneHelperRoutine.java @@ -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; @@ -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; @@ -42,6 +46,7 @@ public final class AutoPruneHelperRoutine implements Routine { private final ModAuditLogWriter modAuditLogWriter; private final Database database; private final List allCategories; + private final Predicate selectYourRolesChannelNamePredicate; /** * Creates a new instance. @@ -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 @@ -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); } }); } @@ -104,8 +107,8 @@ private boolean isRoleFull(Collection members) { return members.size() >= roleFullThreshold; } - private void pruneRole(Role role, List members, ForumChannel helpForum, - Instant when) { + private void pruneRole(Role role, List members, + @Nullable TextChannel selectRoleChannel, Instant when) { List membersShuffled = new ArrayList<>(members); Collections.shuffle(membersShuffled); @@ -128,7 +131,7 @@ private void pruneRole(Role role, List 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) { @@ -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()) @@ -173,4 +181,11 @@ private void warnModsAbout(String message, Guild guild) { modAuditLogWriter.write("Auto-prune helpers", message, null, Instant.now(), guild); } + + private Optional getSelectRolesChannelOptional(JDA jda) { + return jda.getTextChannels() + .stream() + .filter(textChannel -> selectYourRolesChannelNamePredicate.test(textChannel.getName())) + .findFirst(); + } } 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 693ade353e..a5127d9ac2 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 @@ -179,8 +179,9 @@ public void onModalSubmitted(ModalInteractionEvent event, List 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(),