Compare commits

..

40 Commits

Author SHA1 Message Date
Sheldan
91a23f870c [maven-release-plugin] prepare release abstracto-application-1.3.13 2022-05-15 17:38:18 +02:00
Sheldan
b047d3eb49 [AB-xxx] adding another autoload macro for templates 2022-05-15 14:02:17 +02:00
Sheldan
db5b420f0a [maven-release-plugin] prepare for next development iteration 2022-04-07 22:27:00 +02:00
Sheldan
a7e60f6338 [maven-release-plugin] prepare release abstracto-application-1.3.12 2022-04-07 22:26:55 +02:00
Sheldan
eed90c1406 [AB-360] fixing warnings not being stored in case the message was not deliverable 2022-04-07 22:07:35 +02:00
Sheldan
78027ee980 [maven-release-plugin] prepare for next development iteration 2022-02-12 11:37:13 +01:00
Sheldan
e35071d8d5 [maven-release-plugin] prepare release abstracto-application-1.3.11 2022-02-12 11:37:08 +01:00
Sheldan
14865a32f2 [AB-xxx] fixing year 2022-02-12 11:34:41 +01:00
Sheldan
ae3c66384f [AB-xxx] not requiring async voice channel listeners
fixing duplicated error reporting for command received handler
2022-02-10 23:05:35 +01:00
Sheldan
986b65a1e4 [AB-347] adding support for multiple and generic attachments to modmail
fixing editing messages with multiple embeds
fixing message limit also bein imposed on pure embed count
2021-12-25 16:29:23 +01:00
Sheldan
cc898b27bb [AB-353] reducing chance of concurrent modification exception when registering a new metric on the fly 2021-12-25 10:53:55 +01:00
Sheldan
9ce07a1a4a [AB-352] removing conversion tests 2021-12-24 23:49:26 +01:00
Sheldan
ee01a3f07c [AB-352] adding caching to the loading of members for warnings/user notes/mutes. this becomes relevant as members which left the guild are not cached by jda, which means, we retry it again
improved models for templates to simplify the objects passed
2021-12-24 23:41:27 +01:00
Sheldan
9230a13218 [maven-release-plugin] prepare for next development iteration 2021-12-24 13:47:56 +01:00
Sheldan
c248351721 [maven-release-plugin] prepare release abstracto-application-1.3.10 2021-12-24 13:47:52 +01:00
Sheldan
b0376778fe [AB-xxx] adding utility function to change color of a role 2021-12-24 13:44:57 +01:00
Sheldan
7f5d17e4dc [AB-349] supporting multiple embeds in templating mechanism
fixing paginator without embeds
2021-12-08 16:26:11 +01:00
Sheldan
0e7ea25aef [AB-291] refactoring pagination to custom button pagination
adding mutes command
fixing edit message not considering components
reversing the origin comparison in a few button click listeners
fixing ordering of warnings and mutes from converter
2021-12-01 23:22:46 +01:00
Sheldan
f2aa7035aa [AB-307] enabling dependent features and including them into the setup wizard
this comes with the usual limitation, that dependent features must be referenced via importing
2021-11-28 13:19:17 +01:00
Sheldan
c10296251c [AB-327] adding a third state to the result of a system condition: Ignored, this means that the condition was not evaluated, and is neither true nor false, this is for example used when the feature the condition is about is disabled
adding server ID to has level condition parameters
2021-11-27 22:08:12 +01:00
Sheldan
15fb9d95a8 [AB-343] fixing link buttons not being properly supported in templates 2021-11-23 19:57:16 +01:00
Sheldan
79633d7a8e [AB-341] fixing unSuggest not working correctly in case there are votes on the suggestion 2021-11-20 11:44:07 +01:00
Sheldan
cef46737c5 [AB-170] adding emote stat command for single emote
fixing naming of fields in show tracked emotes
2021-10-27 00:25:10 +02:00
Sheldan
c7076795a2 [AB-340] fixing test 2021-10-26 13:59:48 +02:00
Sheldan
9e1527ccd1 [AB-340] adding timestamp support for serverInfo command 2021-10-26 13:53:13 +02:00
Sheldan
326d488059 [maven-release-plugin] prepare for next development iteration 2021-10-25 23:20:39 +02:00
Sheldan
a39b6695a6 [maven-release-plugin] prepare release abstracto-application-1.3.9 2021-10-25 23:20:31 +02:00
Sheldan
362d87778e [AB-339] repurposing the softban command for a ban command with deletion days 2021-10-25 01:11:32 +02:00
Sheldan
0514d355c7 [AB-167] adding warning created events and infraction counter
adding disabling of post targets
adding some logging for message sending failure consumer
2021-10-25 00:04:08 +02:00
Sheldan
8909e8ebe5 [AB-333] providing some dates as separate instants instead of relying on the member attributes
truncating date to day when displaying the date for export emote stats
fixing wrong message when confirming a command and adding missing exception handling
2021-10-14 01:31:52 +02:00
Sheldan
36ca9b11e4 [AB-334] adding vote result to suggestion reminder 2021-10-13 18:00:54 +02:00
Sheldan
4a66b7fc67 [AB-331] showing separate notifications for the decision on a suggestion 2021-09-28 22:20:11 +02:00
Sheldan
b2a94059e2 [AB-332] showing separate message in case there are no warnings 2021-09-28 22:05:51 +02:00
Sheldan
16e6caa1f0 [AB-196] adding confirmation requirement to various commands
refactoring command received handler in order to re-use when confirmation has been given
removing reaction from showSuggestion and remind command
adding log message for message deleted message, in case the member left
all commands from now on must work without the member field from the message, as this is not available when retrieving the message
2021-09-27 00:22:37 +02:00
Sheldan
da1a71ecdc [AB-319] splitting emotes into two separate fields (animated and static) for server info
adding emote display model
2021-09-10 00:41:15 +02:00
Sheldan
0646efe33d [AB-163] fixing printing all possible feature modes in case an unknown one was provided for enableMode/disableMode 2021-09-10 00:21:30 +02:00
Sheldan
db856f2647 [AB-xxx] improving documentation 2021-09-09 02:20:45 +02:00
Sheldan
1d85eb1e7e [AB-297] adding server names to invite deletion log (if available) 2021-09-08 23:04:35 +02:00
Sheldan
5e6999cd45 [AB-328] fixing case sensitivity of allowed invite links 2021-09-07 01:13:34 +02:00
Sheldan
9659a3487a [maven-release-plugin] prepare for next development iteration 2021-09-06 01:54:00 +02:00
298 changed files with 4103 additions and 1377 deletions

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 Sheldan
Copyright (c) 2022 Sheldan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -8,6 +8,7 @@ import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.ConditionService;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.service.SystemCondition;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
@@ -34,6 +35,7 @@ public class MassPingServiceBean implements MassPingService {
private static final String LEVEL_CONDITION_NAME = "HAS_LEVEL";
private static final String LEVEL_CONDITION_USER_ID_PARAMETER = "userId";
private static final String LEVEL_CONDITION_LEVEL_PARAMETER = "level";
private static final String LEVEL_CONDITION_SERVER_PARAMETER = "serverId";
@Autowired
private ConfigService configService;
@@ -85,12 +87,14 @@ public class MassPingServiceBean implements MassPingService {
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(message.getMember());
parameters.put(LEVEL_CONDITION_USER_ID_PARAMETER, userInAServer.getUserInServerId());
parameters.put(LEVEL_CONDITION_LEVEL_PARAMETER, level);
parameters.put(LEVEL_CONDITION_SERVER_PARAMETER, message.getGuild().getIdLong());
ConditionContextInstance contextInstance = ConditionContextInstance
.builder()
.conditionName(LEVEL_CONDITION_NAME)
.parameters(parameters)
.build();
return conditionService.checkConditions(contextInstance);
SystemCondition.Result result = conditionService.checkConditions(contextInstance);
return SystemCondition.Result.consideredSuccessful(result);
}
@Transactional

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>assignable-roles</artifactId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -50,6 +50,7 @@ public class DeleteAssignableRolePlace extends AbstractConditionableCommand {
.templated(true)
.causesReaction(true)
.async(true)
.requiresConfirmation(true)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -11,6 +11,7 @@ import dev.sheldan.abstracto.core.models.ConditionContextInstance;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.RoleDisplay;
import dev.sheldan.abstracto.core.service.ConditionService;
import dev.sheldan.abstracto.core.service.SystemCondition;
import net.dv8tion.jda.api.entities.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -24,6 +25,7 @@ public class AssignableRoleMinimumLevelConditionImpl implements AssignableRoleCo
private final String conditionName = "HAS_LEVEL";
private final String userIdParameter = "userId";
private final String levelParameter = "level";
private final String serverParameter = "serverId";
@Autowired
private ConditionService conditionService;
@@ -34,13 +36,15 @@ public class AssignableRoleMinimumLevelConditionImpl implements AssignableRoleCo
Map<String, Object> parameters = new HashMap<>();
parameters.put(userIdParameter, aUserInAServer.getUserInServerId());
parameters.put(levelParameter, level);
parameters.put(serverParameter, aUserInAServer.getServerReference().getId());
ConditionContextInstance contextInstance = ConditionContextInstance
.builder()
.conditionName(conditionName)
.parameters(parameters)
.build();
return conditionService.checkConditions(contextInstance);
SystemCondition.Result result = conditionService.checkConditions(contextInstance);
return SystemCondition.Result.consideredSuccessful(result);
}
@Override

View File

@@ -219,7 +219,7 @@ public class AssignableRoleButtonClickedListener implements ButtonClickedListene
@Override
public Boolean handlesEvent(ButtonClickedListenerModel model) {
return model.getOrigin().equals(AssignableRolePlaceServiceBean.ASSIGNABLE_ROLE_COMPONENT_ORIGIN);
return AssignableRolePlaceServiceBean.ASSIGNABLE_ROLE_COMPONENT_ORIGIN.equals(model.getOrigin());
}
@Override

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>assignable-roles</artifactId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>assignable-roles-int</artifactId>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>dynamic-activity</artifactId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>dynamic-activity</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>entertainment</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>entertainment</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking</artifactId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -66,6 +66,7 @@ public class SetExpRole extends AbstractConditionableCommand {
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.requiresConfirmation(true)
.parameters(parameters)
.help(helpInfo)
.build();

View File

@@ -52,6 +52,7 @@ public class SyncRoles extends AbstractConditionableCommand {
.module(ExperienceModuleDefinition.EXPERIENCE)
.templated(true)
.async(true)
.requiresConfirmation(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -67,6 +67,7 @@ public class UnSetExpRole extends AbstractConditionableCommand {
.templated(true)
.async(true)
.causesReaction(true)
.requiresConfirmation(true)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -4,8 +4,10 @@ import dev.sheldan.abstracto.core.models.ConditionContext;
import dev.sheldan.abstracto.core.models.ConditionContextInstance;
import dev.sheldan.abstracto.core.models.ConditionContextVariable;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.SystemCondition;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService;
import lombok.extern.slf4j.Slf4j;
@@ -26,6 +28,7 @@ public class HasLevelCondition implements SystemCondition {
public static final String USER_IN_SERVER_ID_VARIABLE_KEY = "userId";
public static final String LEVEL_VARIABLE = "level";
public static final String SERVER_VARIABLE = "serverId";
public static final String HAS_LEVEL_CONDITION_KEY = "HAS_LEVEL";
@Autowired
@@ -34,11 +37,18 @@ public class HasLevelCondition implements SystemCondition {
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private FeatureFlagService featureFlagService;
@Override
public boolean checkCondition(ConditionContextInstance conditionContext) {
public Result checkCondition(ConditionContextInstance conditionContext) {
Map<String, Object> parameters = conditionContext.getParameters();
Long userInServerId = (Long) parameters.get(USER_IN_SERVER_ID_VARIABLE_KEY);
Long serverId = (Long) parameters.get(SERVER_VARIABLE);
Integer level = (Integer) parameters.get(LEVEL_VARIABLE);
if(!featureFlagService.getFeatureFlagValue(ExperienceFeatureDefinition.EXPERIENCE, serverId)) {
return Result.IGNORED;
}
Optional<AUserInAServer> userInServerOptional = userInServerManagementService.loadUserOptional(userInServerId);
if(userInServerOptional.isPresent()) {
AUserInAServer userInServer = userInServerOptional.get();
@@ -47,11 +57,11 @@ public class HasLevelCondition implements SystemCondition {
AUserExperience user = userExperienceManagementService.findUserInServer(userInServer);
boolean conditionResult = user.getCurrentLevel() != null && user.getCurrentLevel().getLevel() >= level;
log.info("Condition evaluated to {}", conditionResult);
return conditionResult;
return Result.fromBoolean(conditionResult);
}
log.info("No user in server object was found. Evaluating has level to false.");
return false;
return Result.FAILED;
}
@Override
@@ -71,9 +81,14 @@ public class HasLevelCondition implements SystemCondition {
.name(LEVEL_VARIABLE)
.type(Integer.class)
.build();
ConditionContextVariable serverVariable = ConditionContextVariable
.builder()
.name(SERVER_VARIABLE)
.type(Long.class)
.build();
return ConditionContext
.builder()
.requiredVariables(Arrays.asList(userIdVariable, levelVariable))
.requiredVariables(Arrays.asList(userIdVariable, levelVariable, serverVariable))
.build();
}
}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking</artifactId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>invite-filter</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -51,6 +51,7 @@ public class RemoveTrackedInviteLinks extends AbstractConditionableCommand {
.module(InviteFilterModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.requiresConfirmation(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -13,5 +13,5 @@ public interface AllowedInviteLinkRepository extends JpaRepository<AllowedInvite
Optional<AllowedInviteLink> findByTargetServerIdAndServer(Long targetServerId, AServer server);
Optional<AllowedInviteLink> findByTargetServerIdAndServer_Id(Long targetServerId, Long serverId);
Optional<AllowedInviteLink> findByCodeAndServer_Id(String code, Long serverId);
Optional<AllowedInviteLink> findByCodeIgnoreCaseAndServer_Id(String code, Long serverId);
}

View File

@@ -21,6 +21,9 @@ import dev.sheldan.abstracto.invitefilter.model.template.listener.DeletedInvite;
import dev.sheldan.abstracto.invitefilter.model.template.listener.DeletedInvitesNotificationModel;
import dev.sheldan.abstracto.invitefilter.service.management.AllowedInviteLinkManagement;
import dev.sheldan.abstracto.invitefilter.service.management.FilteredInviteLinkManagement;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Invite;
@@ -34,7 +37,6 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -222,11 +224,11 @@ public class InviteLinkFilterServiceBean implements InviteLinkFilterService {
return Invite.resolve(jda, extractCode(code)).submit();
}
private void sendDeletionNotification(List<String> codes, Message message) {
private CompletableFuture<Void> sendDeletionNotification(List<InviteToDelete> codes, Message message) {
Long serverId = message.getGuild().getIdLong();
if(!postTargetService.postTargetDefinedInServer(InviteFilterPostTarget.INVITE_DELETE_LOG, serverId)) {
log.info("Post target {} not defined for server {} - not sending invite link deletion notification.", InviteFilterPostTarget.INVITE_DELETE_LOG.getKey(), serverId);
return;
return CompletableFuture.completedFuture(null);
}
DeletedInvitesNotificationModel model = DeletedInvitesNotificationModel
.builder()
@@ -240,21 +242,30 @@ public class InviteLinkFilterServiceBean implements InviteLinkFilterService {
codes.size(), serverId, message.getAuthor().getIdLong(), message.getChannel().getIdLong(), message.getIdLong());
MessageToSend messageToSend = templateService.renderEmbedTemplate(INVITE_LINK_DELETED_NOTIFICATION_EMBED_TEMPLATE_KEY, model, message.getGuild().getIdLong());
List<CompletableFuture<Message>> messageFutures = postTargetService.sendEmbedInPostTarget(messageToSend, InviteFilterPostTarget.INVITE_DELETE_LOG, serverId);
FutureUtils.toSingleFutureGeneric(messageFutures).thenAccept(unused ->
return FutureUtils.toSingleFutureGeneric(messageFutures).thenAccept(unused ->
log.debug("Successfully send notification about deleted invite link in message {}.", message.getIdLong())
).exceptionally(throwable -> {
log.error("Failed to send notification about deleted invite link in message {}.", message.getIdLong());
return null;
});
);
}
private List<DeletedInvite> groupInvites(List<String> codes) {
private List<DeletedInvite> groupInvites(List<InviteToDelete> codes) {
Map<String, String> codeToGuildName = new HashMap<>();
for (InviteToDelete invite: codes) {
if(!codeToGuildName.containsKey(invite.getInviteCode())) {
codeToGuildName.put(invite.getInviteCode(), invite.getGuildName());
}
}
return codes
.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.collect(Collectors.groupingBy(InviteToDelete::getInviteCode, Collectors.counting()))
.entrySet()
.stream()
.map(functionLongEntry -> new DeletedInvite(functionLongEntry.getKey(), functionLongEntry.getValue()))
.map(functionLongEntry -> DeletedInvite
.builder()
.code(functionLongEntry.getKey())
.guildName(codeToGuildName.get(functionLongEntry.getKey()))
.count(functionLongEntry.getValue())
.build())
.collect(Collectors.toList());
}
@@ -291,18 +302,23 @@ public class InviteLinkFilterServiceBean implements InviteLinkFilterService {
ServerUser author = ServerUser.builder().userId(message.getAuthor().getIdLong()).serverId(message.getGuild().getIdLong()).build();
boolean toDelete = false;
Map<Long, String> targetServers = new HashMap<>();
List<String> deletedInvites = new ArrayList<>();
List<InviteToDelete> deletedInvites = new ArrayList<>();
for (Invite invite : invites) {
if (invite.getType().equals(Invite.InviteType.GUILD)
&& isCodeFiltered(invite.getGuild().getIdLong(), author)) {
toDelete = true;
deletedInvites.add(invite.getCode());
InviteToDelete inviteToDelete = InviteToDelete
.builder()
.inviteCode(invite.getCode())
.guildName(invite.getGuild().getName())
.build();
deletedInvites.add(inviteToDelete);
targetServers.put(invite.getGuild().getIdLong(), invite.getGuild().getName());
}
}
List<String> unResolvedInvites = new ArrayList<>();
foundInvites.forEach(possibleUnresolvedInvite -> {
if(invites.stream().noneMatch(invite -> invite.getCode().equals(possibleUnresolvedInvite))) {
if(invites.stream().noneMatch(invite -> invite.getCode().equalsIgnoreCase(possibleUnresolvedInvite))) {
unResolvedInvites.add(possibleUnresolvedInvite);
}
});
@@ -310,7 +326,11 @@ public class InviteLinkFilterServiceBean implements InviteLinkFilterService {
for(String unresolvedInvite : unResolvedInvites) {
if(isCodeFiltered(unresolvedInvite, author)) {
toDelete = true;
deletedInvites.add(unresolvedInvite);
InviteToDelete inviteToDelete = InviteToDelete
.builder()
.inviteCode(unresolvedInvite)
.build();
deletedInvites.add(inviteToDelete);
}
}
if(toDelete) {
@@ -322,7 +342,11 @@ public class InviteLinkFilterServiceBean implements InviteLinkFilterService {
}
boolean sendNotification = featureModeService.featureModeActive(InviteFilterFeatureDefinition.INVITE_FILTER, serverId, InviteFilterMode.FILTER_NOTIFICATIONS);
if(sendNotification) {
sendDeletionNotification(deletedInvites, message);
sendDeletionNotification(deletedInvites, message)
.thenAccept(unused1 -> log.info("Sent invite deletion notification.")).exceptionally(throwable1 -> {
log.error("Failed to send invite deletion notification.");
return null;
});
}
}
}).exceptionally(throwable -> {
@@ -336,4 +360,12 @@ public class InviteLinkFilterServiceBean implements InviteLinkFilterService {
metricService.registerCounter(MESSAGE_INVITE_FILTERED, "Amount of messages containing an invite filtered");
}
@Getter
@Setter
@Builder
private static class InviteToDelete {
private String guildName;
private String inviteCode;
}
}

View File

@@ -16,7 +16,12 @@ public class AllowedInviteLinkManagementBean implements AllowedInviteLinkManagem
@Override
public AllowedInviteLink createAllowedInviteLink(AServer server, Long targetServerId, String code) {
AllowedInviteLink inviteLink = AllowedInviteLink.builder().targetServerId(targetServerId).code(code).server(server).build();
AllowedInviteLink inviteLink = AllowedInviteLink
.builder()
.targetServerId(targetServerId)
.code(code)
.server(server)
.build();
return repository.save(inviteLink);
}
@@ -48,6 +53,6 @@ public class AllowedInviteLinkManagementBean implements AllowedInviteLinkManagem
@Override
public boolean allowedInviteLinkExists(ServerUser serverUser, String code) {
return repository.findByCodeAndServer_Id(code, serverUser.getServerId()).isPresent();
return repository.findByCodeIgnoreCaseAndServer_Id(code, serverUser.getServerId()).isPresent();
}
}

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>invite-filter</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -1,14 +1,17 @@
package dev.sheldan.abstracto.invitefilter.model.template.listener;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@EqualsAndHashCode
@Builder
public class DeletedInvite {
private String code;
private String guildName;
private Long count;
}

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>link-embed</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -77,7 +77,7 @@ public class MessageEmbedDeleteButtonClickedListener implements ButtonClickedLis
@Override
public Boolean handlesEvent(ButtonClickedListenerModel model) {
return model.getOrigin().equals(MessageEmbedServiceBean.MESSAGE_EMBED_DELETE_ORIGIN);
return MessageEmbedServiceBean.MESSAGE_EMBED_DELETE_ORIGIN.equals(model.getOrigin());
}
@Override

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>link-embed</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>logging</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -8,6 +8,7 @@ import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.logging.config.LoggingFeatureDefinition;
import dev.sheldan.abstracto.logging.config.LoggingPostTarget;
import dev.sheldan.abstracto.logging.model.template.MemberJoinLogModel;
@@ -43,7 +44,11 @@ public class JoinLogger implements AsyncJoinListener {
.build();
log.debug("Logging join event for user {} in server {}.", listenerModel.getMember().getIdLong(), listenerModel.getServerId());
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_JOIN_TEMPLATE, model, listenerModel.getServerId());
postTargetService.sendEmbedInPostTarget(messageToSend, LoggingPostTarget.JOIN_LOG, listenerModel.getServerId());
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, LoggingPostTarget.JOIN_LOG, listenerModel.getServerId()))
.exceptionally(throwable -> {
log.error("Failed to send member joining log.", throwable);
return null;
});
return DefaultListenerResult.PROCESSED;
}

View File

@@ -8,6 +8,7 @@ import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.logging.config.LoggingFeatureDefinition;
import dev.sheldan.abstracto.logging.config.LoggingPostTarget;
import lombok.extern.slf4j.Slf4j;
@@ -46,7 +47,11 @@ public class LeaveLogger implements AsyncLeaveListener {
.build();
log.debug("Logging leave event for user {} in server {}.", listenerModel.getUser().getIdLong(), listenerModel.getServerId());
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_LEAVE_TEMPLATE, model, listenerModel.getServerId());
postTargetService.sendEmbedInPostTarget(messageToSend, LoggingPostTarget.LEAVE_LOG, listenerModel.getServerId());
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, LoggingPostTarget.LEAVE_LOG, listenerModel.getServerId()))
.exceptionally(throwable -> {
log.error("Failed to send member leaving log.", throwable);
return null;
});
return DefaultListenerResult.PROCESSED;
}
}

View File

@@ -65,7 +65,10 @@ public class MessageDeleteLogListener implements AsyncMessageDeletedListener {
CachedMessage message = model.getCachedMessage();
memberService.getMemberInServerAsync(model.getServerId(), message.getAuthor().getAuthorId()).thenAccept(member ->
self.executeListener(message, member)
);
).exceptionally(throwable -> {
log.warn("Could not retrieve member {} for message deleted event in guild {}.", message.getAuthor().getAuthorId(), model.getServerId(), throwable);
return null;
});
return DefaultListenerResult.PROCESSED;
}
@@ -82,7 +85,8 @@ public class MessageDeleteLogListener implements AsyncMessageDeletedListener {
.member(authorMember)
.build();
MessageToSend message = templateService.renderEmbedTemplate(MESSAGE_DELETED_TEMPLATE, logModel, messageFromCache.getServerId());
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, LoggingPostTarget.DELETE_LOG, messageFromCache.getServerId())).exceptionally(throwable -> {
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, LoggingPostTarget.DELETE_LOG, messageFromCache.getServerId()))
.exceptionally(throwable -> {
log.error("Failed to send message deleted log.", throwable);
return null;
});
@@ -99,7 +103,7 @@ public class MessageDeleteLogListener implements AsyncMessageDeletedListener {
.build();
MessageToSend attachmentEmbed = templateService.renderEmbedTemplate(MESSAGE_DELETED_ATTACHMENT_TEMPLATE, attachmentLogModel, messageFromCache.getServerId());
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(attachmentEmbed, LoggingPostTarget.DELETE_LOG, messageFromCache.getServerId()))
.exceptionally(throwable -> {
.exceptionally(throwable -> {
log.error("Failed to send message deleted log.", throwable);
return null;
});

View File

@@ -11,6 +11,7 @@ import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.logging.config.LoggingFeatureDefinition;
import dev.sheldan.abstracto.logging.config.LoggingPostTarget;
import dev.sheldan.abstracto.logging.model.template.MessageDeletedAttachmentLog;
@@ -65,7 +66,11 @@ public class MessageEditedListener implements AsyncMessageUpdatedListener {
.member(messageAfter.getMember())
.build();
MessageToSend message = templateService.renderEmbedTemplate(MESSAGE_EDITED_TEMPLATE, lodModel, model.getServerId());
postTargetService.sendEmbedInPostTarget(message, LoggingPostTarget.EDIT_LOG, model.getServerId());
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, LoggingPostTarget.EDIT_LOG, model.getServerId()))
.exceptionally(throwable -> {
log.error("Failed to send message edited log.", throwable);
return null;
});
if(attachmentWasRemoved) {
log.info("Attachment count changed. Old {}, new {}.", attachmentCountBefore, attachmentCountAfter);
@@ -75,7 +80,7 @@ public class MessageEditedListener implements AsyncMessageUpdatedListener {
).collect(Collectors.toList());
log.debug("Logging deletion of {} attachments.", removedAttachments.size());
for (int i = 0; i < removedAttachments.size(); i++) {
MessageDeletedAttachmentLog log = MessageDeletedAttachmentLog
MessageDeletedAttachmentLog attachmentModel = MessageDeletedAttachmentLog
.builder()
.imageUrl(removedAttachments.get(i).getProxyUrl())
.counter(i + 1)
@@ -84,8 +89,12 @@ public class MessageEditedListener implements AsyncMessageUpdatedListener {
.member(messageAfter.getMember())
.build();
MessageToSend attachmentEmbed = templateService.renderEmbedTemplate(MESSAGE_EDITED_ATTACHMENT_REMOVED_TEMPLATE,
log, messageBefore.getServerId());
postTargetService.sendEmbedInPostTarget(attachmentEmbed, LoggingPostTarget.DELETE_LOG, messageBefore.getServerId());
attachmentModel, messageBefore.getServerId());
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(attachmentEmbed, LoggingPostTarget.DELETE_LOG, messageBefore.getServerId()))
.exceptionally(throwable -> {
log.error("Failed to send message edited attachment log.", throwable);
return null;
});
}
}
return DefaultListenerResult.PROCESSED;

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>logging</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation</artifactId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -42,7 +42,7 @@ public class Ban extends AbstractConditionableCommand {
User user = (User) parameters.get(0);
String reason = (String) parameters.get(1);
return banService.banUser(user, reason, commandContext.getAuthor(), commandContext.getMessage())
return banService.banUserWithNotification(user, reason, commandContext.getAuthor(), 0, commandContext.getMessage())
.thenApply(aVoid -> CommandResult.fromSuccess());
}

View File

@@ -17,7 +17,6 @@ import net.dv8tion.jda.api.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -27,7 +26,7 @@ import static dev.sheldan.abstracto.moderation.service.BanService.BAN_EFFECT_KEY
@Component
@Slf4j
public class SoftBan extends AbstractConditionableCommand {
public class BanDelete extends AbstractConditionableCommand {
@Autowired
private BanService banService;
@@ -36,11 +35,9 @@ public class SoftBan extends AbstractConditionableCommand {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
User user = (User) parameters.get(0);
Duration delDays = Duration.ofDays(7);
if(parameters.size() > 1) {
delDays = (Duration) parameters.get(1);
}
return banService.softBanUser(commandContext.getGuild(), user, delDays)
Integer delDays = (Integer) parameters.get(1);
String reason = (String) parameters.get(2);
return banService.banUserWithNotification(user, reason, commandContext.getAuthor(), delDays, commandContext.getMessage())
.thenApply(unused -> CommandResult.fromSuccess());
}
@@ -48,11 +45,12 @@ public class SoftBan extends AbstractConditionableCommand {
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").templated(true).type(User.class).build());
parameters.add(Parameter.builder().name("delDays").templated(true).type(Duration.class).optional(true).build());
parameters.add(Parameter.builder().name("delDays").templated(true).type(Integer.class).build());
parameters.add(Parameter.builder().name("reason").templated(true).type(String.class).remainder(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
List<EffectConfig> effectConfig = Arrays.asList(EffectConfig.builder().position(0).effectKey(BAN_EFFECT_KEY).build());
return CommandConfiguration.builder()
.name("softBan")
.name("banDelete")
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)

View File

@@ -44,6 +44,7 @@ public class DecayAllWarnings extends AbstractConditionableCommand {
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.requiresConfirmation(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -43,6 +43,7 @@ public class DecayWarnings extends AbstractConditionableCommand {
.name("decayWarnings")
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.requiresConfirmation(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)

View File

@@ -0,0 +1,116 @@
package dev.sheldan.abstracto.moderation.command;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.PaginatorService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.converter.MuteEntryConverter;
import dev.sheldan.abstracto.moderation.model.template.command.MuteEntry;
import dev.sheldan.abstracto.moderation.model.template.command.MutesModel;
import dev.sheldan.abstracto.moderation.service.management.MuteManagementService;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class Mutes extends AbstractConditionableCommand {
private static final String NO_MUTES_TEMPLATE_KEY = "mutes_no_mutes_found";
private static final String MUTES_DISPLAY_TEMPLATE_KEY = "mutes_display_response";
@Autowired
private MuteManagementService muteManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
@Autowired
private MuteEntryConverter muteEntryConverter;
@Autowired
private Mutes self;
@Autowired
private PaginatorService paginatorService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<dev.sheldan.abstracto.moderation.model.database.Mute> mutesToDisplay;
if(commandContext.getParameters().getParameters().isEmpty()) {
AServer server = serverManagementService.loadServer(commandContext.getGuild().getIdLong());
mutesToDisplay = muteManagementService.getAllMutes(server);
} else {
Member member = (Member) commandContext.getParameters().getParameters().get(0);
if(!member.getGuild().equals(commandContext.getGuild())) {
throw new EntityGuildMismatchException();
}
mutesToDisplay = muteManagementService.getAllMutesOf(userInServerManagementService.loadOrCreateUser(member));
}
if(mutesToDisplay.isEmpty()) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(NO_MUTES_TEMPLATE_KEY, new Object(), commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
} else {
return muteEntryConverter.fromMutes(mutesToDisplay)
.thenCompose(muteEntries -> self.renderMutes(commandContext, muteEntries)
.thenApply(unused -> CommandResult.fromIgnored()));
}
}
@Transactional
public CompletableFuture<Void> renderMutes(CommandContext commandContext, List<MuteEntry> mutes) {
MutesModel model = (MutesModel) ContextConverter.slimFromCommandContext(commandContext, MutesModel.class);
model.setMutes(mutes);
return paginatorService.createPaginatorFromTemplate(MUTES_DISPLAY_TEMPLATE_KEY, model, commandContext.getChannel(), commandContext.getAuthor().getIdLong());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("member").templated(true).type(Member.class).optional(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("mutes")
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.supportsEmbedException(true)
.async(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return ModerationFeatureDefinition.MUTING;
}
}

View File

@@ -39,7 +39,7 @@ public class MyWarnings extends AbstractConditionableCommand {
public CommandResult execute(CommandContext commandContext) {
MyWarningsModel model = (MyWarningsModel) ContextConverter.fromCommandContext(commandContext, MyWarningsModel.class);
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
Long currentWarnCount = warnManagementService.getActiveWarnsForUser(userInAServer);
Long currentWarnCount = warnManagementService.getActiveWarnCountForUser(userInAServer);
model.setCurrentWarnCount(currentWarnCount);
Long totalWarnCount = warnManagementService.getTotalWarnsForUser(userInAServer);
model.setTotalWarnCount(totalWarnCount);

View File

@@ -34,7 +34,7 @@ public class UnBan extends AbstractConditionableCommand {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
User user = (User) parameters.get(0);
return banService.unBanUser(user, commandContext.getAuthor())
return banService.unBanUserWithNotification(user, commandContext.getAuthor())
.thenApply(aVoid -> CommandResult.fromSuccess());
}

View File

@@ -1,7 +1,5 @@
package dev.sheldan.abstracto.moderation.command;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import com.jagrosh.jdautilities.menu.Paginator;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
@@ -12,9 +10,13 @@ import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.PaginatorService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.converter.WarnEntryConverter;
@@ -34,7 +36,8 @@ import java.util.concurrent.CompletableFuture;
@Component
public class Warnings extends AbstractConditionableCommand {
public static final String WARNINGS_RESPONSE_TEMPLATE = "warnings_response";
public static final String WARNINGS_RESPONSE_TEMPLATE = "warnings_display_response";
public static final String NO_WARNINGS_TEMPLATE_KEY = "warnings_no_warnings_found";
@Autowired
private WarnManagementService warnManagementService;
@@ -47,15 +50,18 @@ public class Warnings extends AbstractConditionableCommand {
@Autowired
private PaginatorService paginatorService;
@Autowired
private EventWaiter eventWaiter;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private Warnings self;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Warning> warnsToDisplay;
@@ -69,21 +75,26 @@ public class Warnings extends AbstractConditionableCommand {
AServer server = serverManagementService.loadServer(commandContext.getGuild());
warnsToDisplay = warnManagementService.getAllWarningsOfServer(server);
}
return warnEntryConverter.fromWarnings(warnsToDisplay).thenApply(warnEntries -> {
self.renderWarnings(commandContext, warnEntries);
return CommandResult.fromIgnored();
});
if(warnsToDisplay.isEmpty()) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(NO_WARNINGS_TEMPLATE_KEY, new Object(), commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
} else {
return warnEntryConverter.fromWarnings(warnsToDisplay)
.thenCompose(warnEntries -> self.renderWarnings(commandContext, warnEntries))
.thenApply(unused -> CommandResult.fromIgnored());
}
}
@Transactional
public void renderWarnings(CommandContext commandContext, List<WarnEntry> warnEntries) {
public CompletableFuture<Void> renderWarnings(CommandContext commandContext, List<WarnEntry> warnEntries) {
WarningsModel model = (WarningsModel) ContextConverter.slimFromCommandContext(commandContext, WarningsModel.class);
model.setWarnings(warnEntries);
Paginator paginator = paginatorService.createPaginatorFromTemplate(WARNINGS_RESPONSE_TEMPLATE, model, eventWaiter, commandContext.getGuild().getIdLong());
paginator.display(commandContext.getChannel());
return paginatorService.createPaginatorFromTemplate(WARNINGS_RESPONSE_TEMPLATE, model, commandContext.getChannel(), commandContext.getAuthor().getIdLong());
}
@Override
@@ -96,7 +107,7 @@ public class Warnings extends AbstractConditionableCommand {
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.causesReaction(true)
.causesReaction(false)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -0,0 +1,24 @@
package dev.sheldan.abstracto.moderation.config;
import dev.sheldan.abstracto.core.service.ExecutorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
@Configuration
public class ModerationListenerConfig {
@Autowired
private ExecutorService executorService;
@Bean(name = "infractionLevelChangedExecutor")
public TaskExecutor infractionLevelChangedExecutor() {
return executorService.setupExecutorFor("infractionLevelChangedListener");
}
@Bean(name = "warningCreatedExecutor")
public TaskExecutor warningCreatedExecutor() {
return executorService.setupExecutorFor("warningCreatedListener");
}
}

View File

@@ -0,0 +1,114 @@
package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.FutureMemberPair;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.model.database.Mute;
import dev.sheldan.abstracto.moderation.model.template.command.MuteEntry;
import dev.sheldan.abstracto.moderation.service.management.MuteManagementService;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@Component
public class MuteEntryConverter {
@Autowired
private MemberService memberService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private MuteManagementService muteManagementService;
@Autowired
private MuteEntryConverter self;
public CompletableFuture<List<MuteEntry>> fromMutes(List<Mute> mutes) {
Map<ServerSpecificId, FutureMemberPair> loadedMutes = new HashMap<>();
List<CompletableFuture<Member>> allFutures = new ArrayList<>();
Map<Long, CompletableFuture<Member>> memberCaching = new HashMap<>();
mutes.forEach(mute -> {
AUserInAServer mutingUser = mute.getMutingUser();
AUserInAServer mutedUser = mute.getMutedUser();
CompletableFuture<Member> mutedFuture;
if(memberCaching.containsKey(mutedUser.getUserInServerId())) {
mutedFuture = memberCaching.get(mutedUser.getUserInServerId());
} else {
mutedFuture = memberService.getMemberInServerAsync(mutedUser);
memberCaching.put(mutedUser.getUserInServerId(), mutedFuture);
}
CompletableFuture<Member> mutingFuture;
if(memberCaching.containsKey(mutingUser.getUserInServerId())) {
mutingFuture = memberCaching.get(mutingUser.getUserInServerId());
} else {
mutingFuture = memberService.getMemberInServerAsync(mutingUser);
memberCaching.put(mutingUser.getUserInServerId(), mutingFuture);
}
FutureMemberPair futurePair = FutureMemberPair
.builder()
.firstMember(mutingFuture)
.secondMember(mutedFuture)
.build();
loadedMutes.put(mute.getMuteId(), futurePair);
allFutures.add(mutingFuture);
allFutures.add(mutedFuture);
});
CompletableFuture<List<MuteEntry>> future = new CompletableFuture<>();
FutureUtils.toSingleFutureGeneric(allFutures)
.whenComplete((unused, throwable) -> future.complete(self.loadFullMuteEntries(loadedMutes)))
.exceptionally(throwable -> {
future.completeExceptionally(throwable);
return null;
});
return future;
}
@Transactional
public List<MuteEntry> loadFullMuteEntries(Map<ServerSpecificId, FutureMemberPair> loadedMuteInfo) {
List<MuteEntry> entries = new ArrayList<>();
List<ServerSpecificId> muteIds = new ArrayList<>(loadedMuteInfo.keySet());
muteIds.sort(Comparator.comparing(ServerSpecificId::getId));
muteIds.forEach(muteInfo -> {
FutureMemberPair memberPair = loadedMuteInfo.get(muteInfo);
Mute mute = muteManagementService.findMute(muteInfo.getId(), muteInfo.getServerId());
Member mutedMember = !memberPair.getSecondMember().isCompletedExceptionally() ? memberPair.getSecondMember().join() : null;
MemberDisplay mutedUser = MemberDisplay
.builder()
.memberMention(mutedMember != null ? mutedMember.getAsMention() : null)
.userId(mute.getMutedUser().getUserReference().getId())
.serverId(mute.getServer().getId())
.build();
Member mutingMember = !memberPair.getFirstMember().isCompletedExceptionally() ? memberPair.getFirstMember().join() : null;
MemberDisplay mutingUser = MemberDisplay
.builder()
.memberMention(mutingMember != null ? mutingMember.getAsMention() : null)
.userId(mute.getMutingUser().getUserReference().getId())
.build();
MuteEntry entry = MuteEntry
.builder()
.mutedUser(mutedUser)
.mutingUser(mutingUser)
.muteId(mute.getMuteId().getId())
.serverId(mute.getMuteId().getServerId())
.reason(mute.getReason())
.muteDate(mute.getMuteDate())
.muteEnded(mute.getMuteEnded())
.muteDuration(Duration.between(mute.getMuteDate(), mute.getMuteTargetDate()))
.build();
entries.add(entry);
});
return entries;
}
}

View File

@@ -1,7 +1,8 @@
package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
@@ -37,18 +38,30 @@ public class UserNotesConverter {
public CompletableFuture<List<NoteEntryModel>> fromNotes(List<UserNote> userNotes){
List<CompletableFuture<Member>> memberFutures = new ArrayList<>();
HashMap<ServerSpecificId, CompletableFuture<Member>> noteMemberMap = new HashMap<>();
Map<Long, CompletableFuture<Member>> memberCaching = new HashMap<>();
userNotes.forEach(userNote -> {
CompletableFuture<Member> memberFuture = memberService.getMemberInServerAsync(userNote.getUser());
memberFutures.add(memberFuture);
noteMemberMap.put(userNote.getUserNoteId(), memberFuture);
AUserInAServer noteUser = userNote.getUser();
CompletableFuture<Member> noteFuture;
if(memberCaching.containsKey(noteUser.getUserInServerId())) {
noteFuture = memberCaching.get(noteUser.getUserInServerId());
} else {
noteFuture = memberService.getMemberInServerAsync(noteUser);
memberCaching.put(noteUser.getUserInServerId(), noteFuture);
}
memberFutures.add(noteFuture);
noteMemberMap.put(userNote.getUserNoteId(), noteFuture);
});
if(userNotes.isEmpty()) {
memberFutures.add(CompletableFuture.completedFuture(null));
}
return FutureUtils.toSingleFutureGeneric(memberFutures).thenApply(aVoid ->
self.loadFullNotes(noteMemberMap)
);
CompletableFuture<List<NoteEntryModel>> future = new CompletableFuture<>();
FutureUtils.toSingleFutureGeneric(memberFutures)
.whenComplete((unused, throwable) -> future.complete(self.loadFullNotes(noteMemberMap)))
.exceptionally(throwable -> {
future.completeExceptionally(throwable);
return null;
});
return future;
}
@Transactional
@@ -58,15 +71,19 @@ public class UserNotesConverter {
CompletableFuture<Member> memberFuture = futureHashMap.get(serverSpecificId);
Member member = !memberFuture.isCompletedExceptionally() ? memberFuture.join() : null;
UserNote note = userNoteManagementService.loadNote(serverSpecificId.getServerId(), serverSpecificId.getId());
FullUserInServer fullUser = FullUserInServer
MemberDisplay display = MemberDisplay
.builder()
.member(member)
.aUserInAServer(note.getUser())
.userId(note.getUser().getUserReference().getId())
.serverId(note.getServer().getId())
.memberMention(member != null ? member.getAsMention() : null)
.build();
NoteEntryModel entryModel = NoteEntryModel
.builder()
.note(note)
.fullUser(fullUser)
.member(display)
.serverId(serverSpecificId.getServerId())
.note(note.getNote())
.noteId(note.getUserNoteId().getId())
.created(note.getCreated())
.build();
entryModels.add(entryModel);
});

View File

@@ -1,8 +1,9 @@
package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.FutureMemberPair;
import dev.sheldan.abstracto.core.models.MemberDisplayModel;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
@@ -14,10 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@Component
@@ -38,49 +36,70 @@ public class WarnEntryConverter {
public CompletableFuture<List<WarnEntry>> fromWarnings(List<Warning> warnings) {
Map<ServerSpecificId, FutureMemberPair> loadedWarnings = new HashMap<>();
List<CompletableFuture<Member>> allFutures = new ArrayList<>();
// TODO maybe optimize to not need to look into the cache twice
Map<Long, CompletableFuture<Member>> memberCaching = new HashMap<>();
warnings.forEach(warning -> {
CompletableFuture<Member> warningMemberFuture = memberService.getMemberInServerAsync(warning.getWarningUser());
CompletableFuture<Member> warnedMemberFuture = memberService.getMemberInServerAsync(warning.getWarnedUser());
FutureMemberPair futurePair = FutureMemberPair.builder().firstMember(warningMemberFuture).secondMember(warnedMemberFuture).build();
AUserInAServer warningUser = warning.getWarningUser();
AUserInAServer warnedUser = warning.getWarnedUser();
CompletableFuture<Member> warnedFuture;
if(memberCaching.containsKey(warnedUser.getUserInServerId())) {
warnedFuture = memberCaching.get(warnedUser.getUserInServerId());
} else {
warnedFuture = memberService.getMemberInServerAsync(warnedUser);
memberCaching.put(warnedUser.getUserInServerId(), warnedFuture);
}
CompletableFuture<Member> warningFuture;
if(memberCaching.containsKey(warningUser.getUserInServerId())) {
warningFuture = memberCaching.get(warningUser.getUserInServerId());
} else {
warningFuture = memberService.getMemberInServerAsync(warningUser);
memberCaching.put(warningUser.getUserInServerId(), warningFuture);
}
FutureMemberPair futurePair = FutureMemberPair.builder().firstMember(warningFuture).secondMember(warnedFuture).build();
loadedWarnings.put(warning.getWarnId(), futurePair);
allFutures.add(warningMemberFuture);
allFutures.add(warnedMemberFuture);
allFutures.add(warningFuture);
allFutures.add(warnedFuture);
});
CompletableFuture<List<WarnEntry>> future = new CompletableFuture<>();
FutureUtils.toSingleFutureGeneric(allFutures)
.whenComplete((unused, throwable) -> future.complete(self.loadFullWarnEntries(loadedWarnings)))
.exceptionally(throwable -> {
future.completeExceptionally(throwable);
return null;
});
.exceptionally(throwable -> {
future.completeExceptionally(throwable);
return null;
});
return future;
}
@Transactional
public List<WarnEntry> loadFullWarnEntries(Map<ServerSpecificId, FutureMemberPair> loadedWarnInfo) {
List<ServerSpecificId> warnIds = new ArrayList<>(loadedWarnInfo.keySet());
warnIds.sort(Comparator.comparing(ServerSpecificId::getId));
List<WarnEntry> entries = new ArrayList<>();
loadedWarnInfo.keySet().forEach(warning -> {
warnIds.forEach(warning -> {
Warning warn = warnManagementService.findById(warning.getId(), warning.getServerId());
FutureMemberPair memberPair = loadedWarnInfo.get(warning);
Member warnedMember = !memberPair.getSecondMember().isCompletedExceptionally() ? memberPair.getSecondMember().join() : null;
MemberDisplayModel warnedUser = MemberDisplayModel
MemberDisplay warnedUser = MemberDisplay
.builder()
.member(warnedMember)
.memberMention(warnedMember != null ? warnedMember.getAsMention() : null)
.userId(warn.getWarnedUser().getUserReference().getId())
.build();
Member warningMember = !memberPair.getFirstMember().isCompletedExceptionally() ? memberPair.getFirstMember().join() : null;
MemberDisplayModel warningUser = MemberDisplayModel
MemberDisplay warningUser = MemberDisplay
.builder()
.member(warningMember)
.memberMention(warningMember != null ? warningMember.getAsMention() : null)
.userId(warn.getWarningUser().getUserReference().getId())
.build();
WarnEntry entry = WarnEntry
.builder()
.warnedUser(warnedUser)
.warningUser(warningUser)
.warning(warn)
.reason(warn.getReason())
.decayDate(warn.getDecayDate())
.decayed(warn.getDecayed())
.warnId(warn.getWarnId().getId())
.warnDate(warn.getWarnDate())
.serverId(warn.getWarnId().getServerId())
.build();
entries.add(entry);
});

View File

@@ -0,0 +1,48 @@
package dev.sheldan.abstracto.moderation.listener.manager;
import dev.sheldan.abstracto.core.listener.ListenerService;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.moderation.listener.InfractionLevelChangedListener;
import dev.sheldan.abstracto.moderation.model.listener.InfractionLevelChangedEventModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Slf4j
public class InfractionLevelChangedListenerManager {
@Autowired(required = false)
private List<InfractionLevelChangedListener> listeners;
@Autowired
private ListenerService listenerService;
@Autowired
@Qualifier("infractionLevelChangedExecutor")
private TaskExecutor infractionLevelChangedExecutor;
public void sendInfractionLevelChangedEvent(Integer newLevel, Integer oldLevel, Long newPoints, Long oldPoints, ServerUser serverUser) {
if(listeners == null || listeners.isEmpty()) {
return;
}
InfractionLevelChangedEventModel model = createInfractionChangedModel(newLevel, oldLevel, oldPoints, newPoints, serverUser);
listeners.forEach(listener -> listenerService.executeFeatureAwareListener(listener, model, infractionLevelChangedExecutor));
}
private InfractionLevelChangedEventModel createInfractionChangedModel(Integer newLevel, Integer oldLevel, Long oldPoints, Long newPoints, ServerUser serverUser) {
return InfractionLevelChangedEventModel
.builder()
.newLevel(newLevel)
.oldLevel(oldLevel)
.oldPoints(oldPoints)
.newPoints(newPoints)
.userId(serverUser.getUserId())
.serverId(serverUser.getServerId())
.build();
}
}

View File

@@ -0,0 +1,53 @@
package dev.sheldan.abstracto.moderation.listener.manager;
import dev.sheldan.abstracto.core.listener.ListenerService;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.moderation.listener.WarningCreatedListener;
import dev.sheldan.abstracto.moderation.model.listener.WarningCreatedEventModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Slf4j
public class WarningCreatedListenerManager {
@Autowired(required = false)
private List<WarningCreatedListener> listeners;
@Autowired
private ListenerService listenerService;
@Autowired
@Qualifier("warningCreatedExecutor")
private TaskExecutor warningCreatedExecutor;
public void sendWarningCreatedEvent(ServerSpecificId warnId, ServerUser warnedUser, ServerUser warningUser,
String reason, ServerChannelMessage warnCommand) {
if(listeners == null || listeners.isEmpty()) {
return;
}
WarningCreatedEventModel model = createWarningCreatedEventModel(warnId, warnedUser, warningUser, reason, warnCommand);
listeners.forEach(listener -> listenerService.executeFeatureAwareListener(listener, model, warningCreatedExecutor));
}
private WarningCreatedEventModel createWarningCreatedEventModel(ServerSpecificId warnId, ServerUser warnedUser,
ServerUser warningUser, String reason, ServerChannelMessage warnCommandMessage) {
return WarningCreatedEventModel
.builder()
.warningId(warnId.getId())
.serverId(warnId.getServerId())
.warningUserId(warningUser.getUserId())
.warnedUserId(warnedUser.getUserId())
.reason(reason)
.warningChannelId(warnCommandMessage.getChannelId())
.warningMessageId(warnCommandMessage.getMessageId())
.build();
}
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.moderation.repository;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.moderation.model.database.Infraction;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface InfractionRepository extends JpaRepository<Infraction, Long> {
List<Infraction> findByUserAndDecayedFalse(AUserInAServer aUserInAServer);
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.moderation.repository;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.moderation.model.database.Mute;
import org.jetbrains.annotations.NotNull;
@@ -18,6 +19,10 @@ public interface MuteRepository extends JpaRepository<Mute, ServerSpecificId> {
List<Mute> findAllByMutedUserAndMuteEndedFalseOrderByMuteId_IdDesc(AUserInAServer aUserInAServer);
List<Mute> findAllByMutedUserOrderByMuteId_IdAsc(AUserInAServer aUserInAServer);
List<Mute> findAllByServerOrderByMuteId_IdAsc(AServer server);
@NotNull
Optional<Mute> findByMuteId_IdAndMuteId_ServerId(Long muteId, Long serverId);
}

View File

@@ -23,6 +23,7 @@ public interface WarnRepository extends JpaRepository<Warning, ServerSpecificId>
Long countByWarnedUser(AUserInAServer aUserInAServer);
Long countByWarnedUserAndDecayedFalse(AUserInAServer aUserInAServer);
List<Warning> findByWarnedUserAndDecayedFalse(AUserInAServer aUserInAServer);
List<Warning> findByWarnedUser(AUserInAServer aUserInAServer);

View File

@@ -17,7 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@@ -51,7 +50,7 @@ public class BanServiceBean implements BanService {
private ChannelService channelService;
@Override
public CompletableFuture<Void> banMember(Member member, String reason, Member banningMember, Message message) {
public CompletableFuture<Void> banMemberWithNotification(Member member, String reason, Member banningMember, Integer deletionDays, Message message) {
BanLog banLog = BanLog
.builder()
.bannedUser(member.getUser())
@@ -59,18 +58,19 @@ public class BanServiceBean implements BanService {
.commandMessage(message)
.reason(reason)
.build();
CompletableFuture<Void> banFuture = banUser(member.getGuild(), member.getUser(), 0, reason);
CompletableFuture<Void> banFuture = banUser(member.getGuild(), member.getUser(), deletionDays, reason);
CompletableFuture<Void> messageFuture = sendBanLogMessage(banLog, member.getGuild().getIdLong(), BAN_LOG_TEMPLATE);
return CompletableFuture.allOf(banFuture, messageFuture);
}
@Override
public CompletableFuture<Void> banUser(User user, String reason, Member banningMember, Message message) {
public CompletableFuture<Void> banUserWithNotification(User user, String reason, Member banningMember, Integer deletionDays, Message message) {
BanLog banLog = BanLog
.builder()
.bannedUser(user)
.banningMember(banningMember)
.commandMessage(message)
.deletionDays(deletionDays)
.reason(reason)
.build();
Guild guild = banningMember.getGuild();
@@ -82,7 +82,7 @@ public class BanServiceBean implements BanService {
.thenAccept(message1 -> log.info("Notified about not being able to send ban notification in server {} and channel {} based on message {} from user {}."
, message.getGuild().getIdLong(), message.getChannel().getIdLong(), message.getIdLong(), message.getAuthor().getIdLong()));
}
CompletableFuture<Void> banFuture = banUser(guild, user, 0, reason);
CompletableFuture<Void> banFuture = banUser(guild, user, deletionDays, reason);
CompletableFuture<Void> messageFuture = sendBanLogMessage(banLog, guild.getIdLong(), BAN_LOG_TEMPLATE);
CompletableFuture.allOf(banFuture, messageFuture)
.thenAccept(unused1 -> returningFuture.complete(null))
@@ -108,7 +108,7 @@ public class BanServiceBean implements BanService {
}
@Override
public CompletableFuture<Void> unBanUser(User user, Member unBanningMember) {
public CompletableFuture<Void> unBanUserWithNotification(User user, Member unBanningMember) {
Guild guild = unBanningMember.getGuild();
UnBanLog banLog = UnBanLog
.builder()
@@ -139,20 +139,14 @@ public class BanServiceBean implements BanService {
}
public CompletableFuture<Void> sendBanLogMessage(BanLog banLog, Long guildId, String template) {
CompletableFuture<Void> completableFuture;
MessageToSend banLogMessage = templateService.renderEmbedTemplate(template, banLog, guildId);
log.debug("Sending ban log message in guild {}.", guildId);
List<CompletableFuture<Message>> notificationFutures = postTargetService.sendEmbedInPostTarget(banLogMessage, ModerationPostTarget.BAN_LOG, guildId);
completableFuture = FutureUtils.toSingleFutureGeneric(notificationFutures);
return completableFuture;
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(banLogMessage, ModerationPostTarget.BAN_LOG, guildId));
}
public CompletableFuture<Void> sendUnBanLogMessage(UnBanLog banLog, Long guildId, String template) {
CompletableFuture<Void> completableFuture;
MessageToSend banLogMessage = templateService.renderEmbedTemplate(template, banLog, guildId);
log.debug("Sending unban log message in guild {}.", guildId);
List<CompletableFuture<Message>> notificationFutures = postTargetService.sendEmbedInPostTarget(banLogMessage, ModerationPostTarget.UN_BAN_LOG, guildId);
completableFuture = FutureUtils.toSingleFutureGeneric(notificationFutures);
return completableFuture;
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(banLogMessage, ModerationPostTarget.UN_BAN_LOG, guildId));
}
}

View File

@@ -0,0 +1,144 @@
package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.config.feature.InfractionFeatureConfig;
import dev.sheldan.abstracto.moderation.config.posttarget.InfractionPostTarget;
import dev.sheldan.abstracto.moderation.listener.manager.InfractionLevelChangedListenerManager;
import dev.sheldan.abstracto.moderation.model.database.Infraction;
import dev.sheldan.abstracto.moderation.model.template.InfractionLevelChangeModel;
import dev.sheldan.abstracto.moderation.service.management.InfractionManagementService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
@Slf4j
public class InfractionServiceBean implements InfractionService {
@Autowired
private InfractionManagementService infractionManagementService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private ConfigService configService;
@Autowired
private ConfigManagementService configManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private PostTargetService postTargetService;
@Autowired
private InfractionServiceBean self;
@Autowired
private InfractionLevelChangedListenerManager infractionLevelChangedListenerManager;
private static final String INFRACTION_NOTIFICATION_TEMPLATE_KEY = "infraction_level_notification";
@Override
public void decayInfraction(Infraction infraction) {
log.info("Decaying infraction {}", infraction.getId());
infraction.setDecayed(true);
infraction.setDecayedDate(Instant.now());
}
@Override
public Long getActiveInfractionPointsForUser(AUserInAServer aUserInAServer) {
List<Infraction> infractions = infractionManagementService.getActiveInfractionsForUser(aUserInAServer);
log.info("Calculating points for user {} in server {} with {} infractions.",
aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId(), infractions.size());
return infractions.stream().collect(Collectors.summarizingLong(Infraction::getPoints)).getCount();
}
@Override
public CompletableFuture<Infraction> createInfractionWithNotification(AUserInAServer aUserInAServer, Long points) {
Infraction createdInfraction = infractionManagementService.createInfraction(aUserInAServer, points);
Long infractionId = createdInfraction.getId();
return createInfractionNotification(aUserInAServer, points)
.thenApply(aBoolean -> self.reloadInfraction(infractionId));
}
@Override
public CompletableFuture<Void> createInfractionNotification(AUserInAServer aUserInAServer, Long points) {
Long serverId = aUserInAServer.getServerReference().getId();
Long currentPoints = getActiveInfractionPointsForUser(aUserInAServer);
Long newPoints = currentPoints + points;
Pair<Integer, Integer> levelChange = infractionLevelChanged(serverId, newPoints, currentPoints);
Integer oldLevel = levelChange.getFirst();
Integer newLevel = levelChange.getSecond();
if(!oldLevel.equals(newLevel)) {
InfractionLevelChangeModel model = InfractionLevelChangeModel
.builder()
.member(MemberDisplay.fromAUserInAServer(aUserInAServer))
.newLevel(newLevel)
.oldLevel(oldLevel)
.oldPoints(currentPoints)
.newPoints(newPoints)
.build();
infractionLevelChangedListenerManager.sendInfractionLevelChangedEvent(newLevel, oldLevel, newPoints, currentPoints, ServerUser.fromAUserInAServer(aUserInAServer));
MessageToSend messageToSend = templateService.renderEmbedTemplate(INFRACTION_NOTIFICATION_TEMPLATE_KEY, model, serverId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, InfractionPostTarget.INFRACTION_NOTIFICATION, serverId));
} else {
return CompletableFuture.completedFuture(null);
}
}
@Transactional
public Infraction reloadInfraction(Long infractionId) {
return infractionManagementService.loadInfraction(infractionId);
}
private Pair<Integer, Integer> infractionLevelChanged(Long serverId, Long newPoints, Long oldPoints) {
List<Long> levelConfig = loadInfractionConfig(serverId);
Integer newLevel = getInfractionLevel(newPoints, levelConfig);
Integer oldLevel = getInfractionLevel(oldPoints, levelConfig);
return Pair.of(oldLevel, newLevel);
}
private List<Long> loadInfractionConfig(Long serverId) {
Long levelAmount = configService.getLongValueOrConfigDefault(InfractionFeatureConfig.INFRACTION_LEVELS, serverId);
List<Long> levelConfig = new ArrayList<>();
for (long i = 1; i <= levelAmount; i++) {
String levelKey = InfractionFeatureConfig.INFRACTION_LEVEL_PREFIX + i;
if(configManagementService.configExists(serverId, levelKey)) {
levelConfig.add(configService.getLongValue(levelKey, serverId));
}
}
return levelConfig;
}
private Integer getInfractionLevel(Long points, List<Long> levelConfig) {
for (int i = 0; i < levelConfig.size(); i++) {
if(points >= levelConfig.get(i)) {
return i;
}
}
return 0;
}
}

View File

@@ -40,10 +40,8 @@ public class KickServiceBean implements KickService {
}
private CompletableFuture<Void> sendKickLog(KickLogModel kickLogModel) {
CompletableFuture<Void> completableFuture;
MessageToSend warnLogMessage = templateService.renderEmbedTemplate(KICK_LOG_TEMPLATE, kickLogModel, kickLogModel.getGuild().getIdLong());
log.debug("Sending kick log message in guild {}.", kickLogModel.getGuild().getIdLong());
completableFuture = FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(warnLogMessage, ModerationPostTarget.KICK_LOG, kickLogModel.getGuild().getIdLong()));
return completableFuture;
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(warnLogMessage, ModerationPostTarget.KICK_LOG, kickLogModel.getGuild().getIdLong()));
}
}

View File

@@ -244,21 +244,15 @@ public class MuteServiceBean implements MuteService {
}
private CompletableFuture<Void> sendMuteLog(MuteContext muteLogModel, AServer server) {
CompletableFuture<Void> completableFuture;
log.debug("Sending mute log to the mute post target.");
MessageToSend message = templateService.renderEmbedTemplate(MUTE_LOG_TEMPLATE, muteLogModel, server.getId());
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, muteLogModel.getContext().getServerId());
completableFuture = FutureUtils.toSingleFutureGeneric(completableFutures);
return completableFuture;
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, muteLogModel.getContext().getServerId()));
}
private CompletableFuture<Void> sendUnMuteLogMessage(UnMuteLog muteLogModel, AServer server) {
CompletableFuture<Void> completableFuture;
log.debug("Sending unMute log for mute {} to the mute posttarget in server {}", muteLogModel.getMute().getMuteId().getId(), server.getId());
MessageToSend message = templateService.renderEmbedTemplate(UN_MUTE_LOG_TEMPLATE, muteLogModel, server.getId());
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, server.getId());
completableFuture = FutureUtils.toSingleFutureGeneric(completableFutures);
return completableFuture;
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, server.getId()));
}
@Override
@@ -313,13 +307,16 @@ public class MuteServiceBean implements MuteService {
finalFuture.complete(null)
).exceptionally(throwable1 -> {
log.error("Unmute log failed to send for mute {} in server {}.", muteId, serverId, throwable1);
finalFuture.complete(null);
finalFuture.completeExceptionally(null);
return null;
});
} else {
finalFuture.complete(null);
}
return null;
}).exceptionally(throwable -> {
finalFuture.completeExceptionally(throwable);
return null;
});
return finalFuture;
@@ -368,7 +365,7 @@ public class MuteServiceBean implements MuteService {
@Override
public void completelyUnMuteUser(AUserInAServer aUserInAServer) {
log.info("Completely unmuting user {} in server {}.", aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId());
List<Mute> allMutesOfUser = muteManagementService.getAllMutesOf(aUserInAServer);
List<Mute> allMutesOfUser = muteManagementService.getAllActiveMutesOf(aUserInAServer);
allMutesOfUser.forEach(mute -> {
mute.setMuteEnded(true);
cancelUnMuteJob(mute);

View File

@@ -100,9 +100,13 @@ public class ReactionReportServiceBean implements ReactionReportService {
@Transactional
public void createReactionReportInDb(CachedMessage cachedMessage, Message reportMessage, ServerUser reporter) {
log.info("Creation reaction report in message {} about message {} in database.", reportMessage.getIdLong(), cachedMessage.getMessageId());
reactionReportManagementService.createReactionReport(cachedMessage, reportMessage);
updateModerationUserReportCooldown(reporter);
if(reportMessage == null) {
log.info("Creation reaction report about message {} was not sent - post target might be disabled in server {}.", cachedMessage.getMessageId(), cachedMessage.getServerId());
} else {
log.info("Creation reaction report in message {} about message {} in database.", reportMessage.getIdLong(), cachedMessage.getMessageId());
reactionReportManagementService.createReactionReport(cachedMessage, reportMessage);
updateModerationUserReportCooldown(reporter);
}
}
@Transactional

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.models.FutureMemberPair;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AServer;
@@ -12,16 +13,20 @@ import dev.sheldan.abstracto.core.service.management.UserInServerManagementServi
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.config.feature.WarningDecayFeatureConfig;
import dev.sheldan.abstracto.moderation.config.feature.WarningFeatureConfig;
import dev.sheldan.abstracto.moderation.config.feature.mode.WarnDecayMode;
import dev.sheldan.abstracto.moderation.config.feature.mode.WarningMode;
import dev.sheldan.abstracto.moderation.config.posttarget.WarnDecayPostTarget;
import dev.sheldan.abstracto.moderation.config.posttarget.WarningPostTarget;
import dev.sheldan.abstracto.moderation.listener.manager.WarningCreatedListenerManager;
import dev.sheldan.abstracto.moderation.model.database.Infraction;
import dev.sheldan.abstracto.moderation.model.database.Warning;
import dev.sheldan.abstracto.moderation.model.template.command.WarnContext;
import dev.sheldan.abstracto.moderation.model.template.command.WarnNotification;
import dev.sheldan.abstracto.moderation.model.template.job.WarnDecayLogModel;
import dev.sheldan.abstracto.moderation.model.template.job.WarnDecayWarning;
import dev.sheldan.abstracto.moderation.model.template.listener.WarnDecayMemberNotificationModel;
import dev.sheldan.abstracto.moderation.service.management.InfractionManagementService;
import dev.sheldan.abstracto.moderation.service.management.WarnManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
@@ -85,6 +90,18 @@ public class WarnServiceBean implements WarnService {
@Autowired
private WarnServiceBean self;
@Autowired
private InfractionService infractionService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private InfractionManagementService infractionManagementService;
@Autowired
private WarningCreatedListenerManager warningCreatedListenerManager;
public static final String WARN_LOG_TEMPLATE = "warn_log";
public static final String WARN_NOTIFICATION_TEMPLATE = "warn_notification";
public static final String WARNINGS_COUNTER_KEY = "WARNINGS";
@@ -100,22 +117,41 @@ public class WarnServiceBean implements WarnService {
Member warningMember = context.getMember();
Guild guild = warnedMember.getGuild();
log.info("User {} is warning {} in server {}", warnedMember.getId(), warningMember.getId(), guild.getIdLong());
WarnNotification warnNotification = WarnNotification.builder().reason(context.getReason()).warnId(warningId).serverName(guild.getName()).build();
WarnNotification warnNotification = WarnNotification
.builder()
.reason(context.getReason())
.warnId(warningId)
.serverName(guild.getName())
.build();
Long serverId = server.getId();
String warnNotificationMessage = templateService.renderTemplate(WARN_NOTIFICATION_TEMPLATE, warnNotification, server.getId());
List<CompletableFuture<Message>> futures = new ArrayList<>();
futures.add(messageService.sendMessageToUser(warnedMember.getUser(), warnNotificationMessage));
List<CompletableFuture> futures = new ArrayList<>();
CompletableFuture<Void> notificationFuture = new CompletableFuture<>();
messageService.sendMessageToUser(warnedMember.getUser(), warnNotificationMessage).whenComplete((message, throwable) -> {
if(throwable != null) {
log.warn("Failed to notify user {} of warning {} in guild {}.", warnedMember.getId(), warningId, serverId);
}
notificationFuture.complete(null);
});
futures.add(notificationFuture);
log.debug("Logging warning for server {}.", server.getId());
if(featureFlagService.getFeatureFlagValue(ModerationFeatureDefinition.INFRACTIONS, serverId)) {
Long infractionPoints = configService.getLongValueOrConfigDefault(WarningFeatureConfig.WARN_INFRACTION_POINTS, serverId);
AUserInAServer warnedUser = userInServerManagementService.loadOrCreateUser(warnedMember);
warnedUser.setServerReference(server);
futures.add(infractionService.createInfractionWithNotification(warnedUser, infractionPoints)
.thenAccept(infraction -> context.setInfractionId(infraction.getId())));
}
MessageToSend message = templateService.renderEmbedTemplate(WARN_LOG_TEMPLATE, context, server.getId());
futures.addAll(postTargetService.sendEmbedInPostTarget(message, WarningPostTarget.WARN_LOG, context.getGuild().getIdLong()));
return FutureUtils.toSingleFutureGeneric(futures);
return FutureUtils.toSingleFuture(futures);
}
@Override
public CompletableFuture<Void> warnUserWithLog(WarnContext context) {
return notifyAndLogFullUserWarning(context).thenAccept(aVoid ->
self.persistWarning(context)
);
return notifyAndLogFullUserWarning(context)
.thenAccept(aVoid -> self.persistWarning(context));
}
@Transactional
@@ -124,8 +160,15 @@ public class WarnServiceBean implements WarnService {
context.getWarnId(), context.getGuild().getId(), context.getWarnedMember().getId(), context.getMember().getId());
AUserInAServer warnedUser = userInServerManagementService.loadOrCreateUser(context.getWarnedMember());
AUserInAServer warningUser = userInServerManagementService.loadOrCreateUser(context.getMember());
warnManagementService.createWarning(warnedUser, warningUser, context.getReason(), context.getWarnId());
Warning createdWarning = warnManagementService.createWarning(warnedUser, warningUser, context.getReason(), context.getWarnId());
if(context.getInfractionId() != null) {
Infraction infraction = infractionManagementService.loadInfraction(context.getInfractionId());
createdWarning.setInfraction(infraction);
}
ServerUser warnedServerUser = ServerUser.fromAUserInAServer(warnedUser);
ServerUser warningServerUser = ServerUser.fromAUserInAServer(warnedUser);
ServerChannelMessage commandMessage = ServerChannelMessage.fromMessage(context.getMessage());
warningCreatedListenerManager.sendWarningCreatedEvent(createdWarning.getWarnId(), warnedServerUser, warningServerUser, context.getReason(), commandMessage);
}
@Override
@@ -235,10 +278,13 @@ public class WarnServiceBean implements WarnService {
}
@Override
public void decayWarning(Warning warning, Instant now) {
log.debug("Decaying warning {} in server {} with date {}.", warning.getWarnId().getId(), warning.getWarnId().getServerId(), now);
warning.setDecayDate(now);
public void decayWarning(Warning warning, Instant decayDate) {
log.debug("Decaying warning {} in server {} with date {}.", warning.getWarnId().getId(), warning.getWarnId().getServerId(), decayDate);
warning.setDecayDate(decayDate);
warning.setDecayed(true);
if(warning.getInfraction() != null) {
infractionService.decayInfraction(warning.getInfraction());
}
}
private CompletableFuture<Void> logDecayedWarnings(AServer server, List<Warning> warningsToDecay) {
@@ -269,6 +315,9 @@ public class WarnServiceBean implements WarnService {
return null;
});
return null;
}).exceptionally(throwable -> {
sendingFuture.completeExceptionally(throwable);
return null;
});
return sendingFuture;
@@ -304,8 +353,7 @@ public class WarnServiceBean implements WarnService {
.warnings(warnDecayWarnings)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(WARN_DECAY_LOG_TEMPLATE_KEY, warnDecayLogModel, serverId);
List<CompletableFuture<Message>> messageFutures = postTargetService.sendEmbedInPostTarget(messageToSend, WarnDecayPostTarget.DECAY_LOG, server.getId());
return FutureUtils.toSingleFutureGeneric(messageFutures);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, WarnDecayPostTarget.DECAY_LOG, server.getId()));
}
@Override

View File

@@ -0,0 +1,38 @@
package dev.sheldan.abstracto.moderation.service.management;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.moderation.model.database.Infraction;
import dev.sheldan.abstracto.moderation.repository.InfractionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class InfractionManagementServiceBean implements InfractionManagementService{
@Autowired
private InfractionRepository infractionRepository;
@Override
public Infraction createInfraction(AUserInAServer aUserInAServer, Long points) {
Infraction infraction = Infraction
.builder()
.user(aUserInAServer)
.server(aUserInAServer.getServerReference())
.points(points)
.build();
return infractionRepository.save(infraction);
}
@Override
public List<Infraction> getActiveInfractionsForUser(AUserInAServer aUserInAServer) {
return infractionRepository.findByUserAndDecayedFalse(aUserInAServer);
}
@Override
public Infraction loadInfraction(Long infraction) {
return infractionRepository.getOne(infraction);
}
}

View File

@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.moderation.service.management;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.AServerAChannelMessage;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.moderation.model.database.Mute;
@@ -84,9 +85,19 @@ public class MuteManagementServiceBean implements MuteManagementService {
}
@Override
public List<Mute> getAllMutesOf(AUserInAServer aUserInAServer) {
public List<Mute> getAllActiveMutesOf(AUserInAServer aUserInAServer) {
return muteRepository.findAllByMutedUserAndMuteEndedFalseOrderByMuteId_IdDesc(aUserInAServer);
}
@Override
public List<Mute> getAllMutesOf(AUserInAServer aUserInAServer) {
return muteRepository.findAllByMutedUserOrderByMuteId_IdAsc(aUserInAServer);
}
@Override
public List<Mute> getAllMutes(AServer server) {
return muteRepository.findAllByServerOrderByMuteId_IdAsc(server);
}
}

View File

@@ -70,10 +70,15 @@ public class WarnManagementServiceBean implements WarnManagementService {
}
@Override
public Long getActiveWarnsForUser(AUserInAServer aUserInAServer) {
public Long getActiveWarnCountForUser(AUserInAServer aUserInAServer) {
return warnRepository.countByWarnedUserAndDecayedFalse(aUserInAServer);
}
@Override
public List<Warning> getActiveWarnsForUser(AUserInAServer aUserInAServer) {
return warnRepository.findByWarnedUserAndDecayedFalse(aUserInAServer);
}
@Override
public Optional<Warning> findByIdOptional(Long id, Long serverId) {
return warnRepository.findByWarnId_IdAndWarnId_ServerId(id, serverId);

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,20 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<property name="moderationModule" value="(SELECT id FROM module WHERE name = 'moderation')"/>
<property name="moderationFeature" value="(SELECT id FROM feature WHERE key = 'moderation')"/>
<changeSet author="Sheldan" id="mutes-command">
<insert tableName="command">
<column name="name" value="mutes"/>
<column name="module_id" valueComputed="${moderationModule}"/>
<column name="feature_id" valueComputed="${moderationFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,12 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
<include file="update/update.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="feature.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,14 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="infraction_feature-insertion">
<insert tableName="feature">
<column name="key" value="infractions"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,46 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="infraction-table">
<createTable tableName="infraction">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="pk_infraction"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="points" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="decayed_date" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="decayed" type="BOOLEAN"/>
<column name="infraction_user_in_server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="infraction" constraintName="fk_infraction_server"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="infraction_user_in_server_id" baseTableName="infraction" constraintName="fk_infraction_user_in_server"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS infraction_update_trigger ON infraction;
CREATE TRIGGER infraction_update_trigger BEFORE UPDATE ON infraction FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS infraction_insert_trigger ON infraction;
CREATE TRIGGER infraction_insert_trigger BEFORE INSERT ON infraction FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,11 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="infraction.xml" relativeToChangelogFile="true"/>
<include file="warning.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,17 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="warning-infraction_id">
<addColumn tableName="warning">
<column name="infraction_id" type="BIGINT" />
</addColumn>
<addForeignKeyConstraint baseColumnNames="infraction_id" baseTableName="warning" constraintName="fk_warning_infraction"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="infraction" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -6,10 +6,10 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="warn-decay-job-update_schedule">
<update tableName="scheduler_job">
<column name="cron_expression" value="0 0 0 * * ?"/>
<where>name='warnDecayJob'</where>
<changeSet author="Sheldan" id="command-rename_soft_ban">
<update tableName="command">
<column name="name" value="banDelete"/>
<where>name='softBan'</where>
</update>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -11,4 +11,6 @@
<include file="1.2.15/collection.xml" relativeToChangelogFile="true"/>
<include file="1.2.16/collection.xml" relativeToChangelogFile="true"/>
<include file="1.3.4/collection.xml" relativeToChangelogFile="true"/>
<include file="1.3.9/collection.xml" relativeToChangelogFile="true"/>
<include file="1.3.10/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -7,6 +7,9 @@ abstracto.systemConfigs.reactionReportCooldownSeconds.longValue=300
abstracto.featureFlags.moderation.featureName=moderation
abstracto.featureFlags.moderation.enabled=false
abstracto.featureFlags.infractions.featureName=infractions
abstracto.featureFlags.infractions.enabled=false
abstracto.featureFlags.reportReactions.featureName=reportReactions
abstracto.featureFlags.reportReactions.enabled=false
@@ -42,6 +45,31 @@ abstracto.featureModes.warnDecayLogging.featureName=warnings
abstracto.featureModes.warnDecayLogging.mode=warnDecayLogging
abstracto.featureModes.warnDecayLogging.enabled=true
abstracto.featureModes.infractionReporting.featureName=infractions
abstracto.featureModes.infractionReporting.mode=infractionReporting
abstracto.featureModes.infractionReporting.enabled=true
abstracto.systemConfigs.infractionLvl1.name=infractionLvl1
abstracto.systemConfigs.infractionLvl1.longValue=10
abstracto.systemConfigs.infractionLvl2.name=infractionLvl2
abstracto.systemConfigs.infractionLvl2.longValue=20
abstracto.systemConfigs.infractionLvl3.name=infractionLvl3
abstracto.systemConfigs.infractionLvl3.longValue=30
abstracto.systemConfigs.infractionLvl4.name=infractionLvl4
abstracto.systemConfigs.infractionLvl4.longValue=40
abstracto.systemConfigs.infractionLvl5.name=infractionLvl5
abstracto.systemConfigs.infractionLvl5.longValue=50
abstracto.systemConfigs.infractionLevels.name=infractionLevels
abstracto.systemConfigs.infractionLevels.longValue=5
abstracto.systemConfigs.warnInfractionPoints.name=warnInfractionPoints
abstracto.systemConfigs.warnInfractionPoints.longValue=0
abstracto.featureModes.automaticWarnDecayLogging.featureName=warnDecay
abstracto.featureModes.automaticWarnDecayLogging.mode=automaticWarnDecayLogging
abstracto.featureModes.automaticWarnDecayLogging.enabled=true

View File

@@ -5,7 +5,6 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.moderation.service.BanService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
@@ -41,7 +40,7 @@ public class BanTest {
public void testBanWithReason() {
String customReason = "reason2";
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(bannedMember, customReason));
when(banService.banUser(eq(bannedMember), eq(customReason), banLogModelCaptor.capture(), any(Message.class))).thenReturn(CompletableFuture.completedFuture(null));
when(banService.banUserWithNotification(eq(bannedMember), eq(customReason), banLogModelCaptor.capture(), eq(0), any(Message.class))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
Member banningMember = banLogModelCaptor.getValue();
Assert.assertEquals(parameters.getAuthor(), banningMember);

View File

@@ -49,7 +49,7 @@ public class MyWarningsTest {
Long activeWarnCount = 8L;
AUserInAServer aUserInAServer = Mockito.mock(AUserInAServer.class);
when(userInServerManagementService.loadOrCreateUser(noParameter.getAuthor())).thenReturn(aUserInAServer);
when(warnManagementService.getActiveWarnsForUser(aUserInAServer)).thenReturn(activeWarnCount);
when(warnManagementService.getActiveWarnCountForUser(aUserInAServer)).thenReturn(activeWarnCount);
Long totalWarnCount = 10L;
when(warnManagementService.getTotalWarnsForUser(aUserInAServer)).thenReturn(totalWarnCount);
CommandResult result = testUnit.execute(noParameter);

View File

@@ -1,125 +0,0 @@
package dev.sheldan.abstracto.moderation.command;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import com.jagrosh.jdautilities.menu.Paginator;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.PaginatorService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.moderation.converter.WarnEntryConverter;
import dev.sheldan.abstracto.moderation.model.database.Warning;
import dev.sheldan.abstracto.moderation.model.template.command.WarnEntry;
import dev.sheldan.abstracto.moderation.model.template.command.WarningsModel;
import dev.sheldan.abstracto.moderation.service.management.WarnManagementService;
import net.dv8tion.jda.api.entities.Member;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class WarningsTest {
@InjectMocks
private Warnings testUnit;
@Mock
private WarnManagementService warnManagementService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private WarnEntryConverter warnEntryConverter;
@Mock
private PaginatorService paginatorService;
@Mock
private ServerManagementService serverManagementService;
@Mock
private EventWaiter eventWaiter;
@Captor
private ArgumentCaptor<WarningsModel> captor;
@Mock
private Warnings self;
private static final Long SERVER_ID = 1L;
@Test
public void testNoParametersForWarningsCommand(){
CommandContext noParams = CommandTestUtilities.getNoParameters();
Warning firstWarning = Mockito.mock(Warning.class);
WarnEntry firstModelWarning = Mockito.mock(WarnEntry.class);
Warning secondWarning = Mockito.mock(Warning.class);
WarnEntry secondModelWarning = Mockito.mock(WarnEntry.class);
List<Warning> warningsToDisplay = Arrays.asList(firstWarning, secondWarning);
List<WarnEntry> modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning);
AServer server = Mockito.mock(AServer.class);
when(serverManagementService.loadServer(noParams.getGuild())).thenReturn(server);
when(warnManagementService.getAllWarningsOfServer(server)).thenReturn(warningsToDisplay);
when(warnEntryConverter.fromWarnings(warningsToDisplay)).thenReturn(CompletableFuture.completedFuture(modelWarnings));
CompletableFuture<CommandResult> result = testUnit.executeAsync(noParams);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
verify(self, times(1)).renderWarnings(noParams, modelWarnings);
}
@Test
public void testWarningsRendering() {
CommandContext noParams = CommandTestUtilities.getNoParameters();
WarnEntry firstModelWarning = Mockito.mock(WarnEntry.class);
WarnEntry secondModelWarning = Mockito.mock(WarnEntry.class);
Paginator paginator = Mockito.mock(Paginator.class);
when(noParams.getGuild().getIdLong()).thenReturn(SERVER_ID);
when(paginatorService.createPaginatorFromTemplate(eq(Warnings.WARNINGS_RESPONSE_TEMPLATE), captor.capture(), eq(eventWaiter), eq(SERVER_ID))).thenReturn(paginator);
List<WarnEntry> modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning);
testUnit.renderWarnings(noParams, modelWarnings);
WarningsModel warningsModel = captor.getValue();
Assert.assertEquals(firstModelWarning, warningsModel.getWarnings().get(0));
Assert.assertEquals(secondModelWarning, warningsModel.getWarnings().get(1));
}
@Test
public void testExecuteWarningsForMember(){
Member member = Mockito.mock(Member.class);
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(member));
when(member.getGuild()).thenReturn(parameters.getGuild());
AUserInAServer warnedUser = Mockito.mock(AUserInAServer.class);
Warning firstWarning = Mockito.mock(Warning.class);
WarnEntry firstModelWarning = Mockito.mock(WarnEntry.class);
Warning secondWarning = Mockito.mock(Warning.class);
WarnEntry secondModelWarning = Mockito.mock(WarnEntry.class);
List<Warning> warningsToDisplay = Arrays.asList(firstWarning, secondWarning);
List<WarnEntry> modelWarnings = Arrays.asList(firstModelWarning, secondModelWarning);
when(userInServerManagementService.loadOrCreateUser(member)).thenReturn(warnedUser);
when(warnManagementService.getAllWarnsForUser(warnedUser)).thenReturn(warningsToDisplay);
when(warnEntryConverter.fromWarnings(warningsToDisplay)).thenReturn(CompletableFuture.completedFuture(modelWarnings));
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
verify(self, times(1)).renderWarnings(parameters, modelWarnings);
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,92 +0,0 @@
package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.moderation.model.database.UserNote;
import dev.sheldan.abstracto.moderation.model.template.command.NoteEntryModel;
import dev.sheldan.abstracto.moderation.service.management.UserNoteManagementService;
import net.dv8tion.jda.api.entities.Member;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class UserNotesConverterTest {
@InjectMocks
private UserNotesConverter testUnit;
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private MemberService memberService;
@Mock
private UserNotesConverter self;
@Mock
private UserNoteManagementService userNoteManagementService;
private static final Long SERVER_ID = 3L;
private static final Long USER_NOTE_ID = 4L;
@Test
public void testWithEmptyList() {
CompletableFuture<List<NoteEntryModel>> entryModels = testUnit.fromNotes(Collections.emptyList());
Assert.assertEquals(0, entryModels.join().size());
}
@Test
public void testWithSomeUserNotes() {
AUserInAServer userInAServer = Mockito.mock(AUserInAServer.class);
Member member = Mockito.mock(Member.class);
when(memberService.getMemberInServerAsync(userInAServer)).thenReturn(CompletableFuture.completedFuture(member));
UserNote firstNote = Mockito.mock(UserNote.class);
when(firstNote.getUser()).thenReturn(userInAServer);
UserNote secondNote = Mockito.mock(UserNote.class);
when(secondNote.getUser()).thenReturn(userInAServer);
testUnit.fromNotes(Arrays.asList(firstNote, secondNote));
verify(self, times(1)).loadFullNotes(any());
}
@Test
public void testLoadingFullNotes() {
AUserInAServer userInAServer = Mockito.mock(AUserInAServer.class);
Member member = Mockito.mock(Member.class);
UserNote note1 = Mockito.mock(UserNote.class);
UserNote note2 = Mockito.mock(UserNote.class);
when(note1.getUser()).thenReturn(userInAServer);
when(note2.getUser()).thenReturn(userInAServer);
ServerSpecificId firstUserNoteId = new ServerSpecificId(SERVER_ID, USER_NOTE_ID);
ServerSpecificId secondUserNoteId = new ServerSpecificId(SERVER_ID, USER_NOTE_ID + 1);
HashMap<ServerSpecificId, CompletableFuture<Member>> map = new HashMap<>();
map.put(firstUserNoteId, CompletableFuture.completedFuture(member));
map.put(secondUserNoteId, CompletableFuture.completedFuture(member));
when(userNoteManagementService.loadNote(SERVER_ID, USER_NOTE_ID)).thenReturn(note1);
when(userNoteManagementService.loadNote(SERVER_ID, USER_NOTE_ID + 1)).thenReturn(note2);
List<NoteEntryModel> models = testUnit.loadFullNotes(map);
Assert.assertEquals(2, models.size());
NoteEntryModel firstEntry = models.get(0);
Assert.assertEquals(member, firstEntry.getFullUser().getMember());
Assert.assertEquals(userInAServer, firstEntry.getFullUser().getAUserInAServer());
NoteEntryModel secondEntry = models.get(1);
Assert.assertEquals(member, secondEntry.getFullUser().getMember());
Assert.assertEquals(userInAServer, secondEntry.getFullUser().getAUserInAServer());
}
}

View File

@@ -1,128 +0,0 @@
package dev.sheldan.abstracto.moderation.converter;
import dev.sheldan.abstracto.core.models.FutureMemberPair;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.moderation.model.database.Warning;
import dev.sheldan.abstracto.moderation.model.template.command.WarnEntry;
import dev.sheldan.abstracto.moderation.service.management.WarnManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
@Slf4j
public class WarnEntryConverterTest {
@InjectMocks
private WarnEntryConverter testUnit;
@Mock
private MemberService memberService;
@Mock
private WarnEntryConverter self;
@Mock
private WarnManagementService warnManagementService;
@Mock
private UserInServerManagementService userInServerManagementService;
private static final Long SERVER_ID = 5L;
private static final Long WARN_ID_1 = 6L;
private static final Long WARN_ID_2 = 7L;
private static final Long USER_ID_1 = 8L;
private static final Long USER_ID_2 = 9L;
@Test
public void testWithEmptyList() {
CompletableFuture<List<WarnEntry>> entryModels = testUnit.fromWarnings(Collections.emptyList());
Assert.assertEquals(0, entryModels.join().size());
}
@Test
public void testWithSomeWarnings() {
AUserInAServer warnedUser = Mockito.mock(AUserInAServer.class);
AUserInAServer warningUser = Mockito.mock(AUserInAServer.class);
Member warnedMember = Mockito.mock(Member.class);
Member warningMember = Mockito.mock(Member.class);
when(memberService.getMemberInServerAsync(warnedUser)).thenReturn(CompletableFuture.completedFuture(warnedMember));
when(memberService.getMemberInServerAsync(warningUser)).thenReturn(CompletableFuture.completedFuture(warningMember));
Warning firstWarning = Mockito.mock(Warning.class);
when(firstWarning.getWarningUser()).thenReturn(warningUser);
when(firstWarning.getWarnedUser()).thenReturn(warnedUser);
Warning secondWarning = Mockito.mock(Warning.class);
when(secondWarning.getWarningUser()).thenReturn(warningUser);
when(secondWarning.getWarnedUser()).thenReturn(warnedUser);
List<WarnEntry> loaded = new ArrayList<>();
when(self.loadFullWarnEntries(any())).thenReturn(loaded);
CompletableFuture<List<WarnEntry>> future = testUnit.fromWarnings(Arrays.asList(firstWarning, secondWarning));
List<WarnEntry> entries = future.join();
Assert.assertFalse(future.isCompletedExceptionally());
Assert.assertEquals(loaded, entries);
verify(self, times(1)).loadFullWarnEntries(any());
}
@Test
public void testLoadingFullWarnings() {
AUserInAServer warnedUser = Mockito.mock(AUserInAServer.class);
AUserInAServer warningUser = Mockito.mock(AUserInAServer.class);
AUser firstUser = Mockito.mock(AUser.class);
when(firstUser.getId()).thenReturn(USER_ID_1);
when(warnedUser.getUserReference()).thenReturn(firstUser);
AUser secondUser = Mockito.mock(AUser.class);
when(secondUser.getId()).thenReturn(USER_ID_2);
when(warningUser.getUserReference()).thenReturn(secondUser);
Member warningMember = Mockito.mock(Member.class);
Member warnedMember = Mockito.mock(Member.class);
Warning warning1 = Mockito.mock(Warning.class);
Warning warning2 = Mockito.mock(Warning.class);
ServerSpecificId firstWarnId = new ServerSpecificId(SERVER_ID, WARN_ID_1);
when(warning1.getWarnId()).thenReturn(firstWarnId);
when(warning1.getWarningUser()).thenReturn(warningUser);
when(warning1.getWarnedUser()).thenReturn(warnedUser);
ServerSpecificId secondWarnId = new ServerSpecificId(SERVER_ID, WARN_ID_2);
when(warning2.getWarnId()).thenReturn(secondWarnId);
when(warning2.getWarningUser()).thenReturn(warningUser);
when(warning2.getWarnedUser()).thenReturn(warnedUser);
HashMap<ServerSpecificId, FutureMemberPair> map = new HashMap<>();
FutureMemberPair memberPair = Mockito.mock(FutureMemberPair.class);
when(memberPair.getFirstMember()).thenReturn(CompletableFuture.completedFuture(warningMember));
when(memberPair.getSecondMember()).thenReturn(CompletableFuture.completedFuture(warnedMember));
map.put(firstWarnId, memberPair);
map.put(secondWarnId, memberPair);
when(warnManagementService.findById(WARN_ID_1, SERVER_ID)).thenReturn(warning1);
when(warnManagementService.findById(WARN_ID_2, SERVER_ID)).thenReturn(warning2);
List<WarnEntry> models = testUnit.loadFullWarnEntries(map);
Assert.assertEquals(2, models.size());
WarnEntry firstEntry = models.get(0);
Assert.assertEquals(warningMember, firstEntry.getWarningUser().getMember());
Assert.assertEquals(warnedMember, firstEntry.getWarnedUser().getMember());
Assert.assertEquals(USER_ID_1, firstEntry.getWarnedUser().getUserId());
Assert.assertEquals(USER_ID_2, firstEntry.getWarningUser().getUserId());
Assert.assertEquals(WARN_ID_1, firstEntry.getWarning().getWarnId().getId());
Assert.assertEquals(SERVER_ID, firstEntry.getWarning().getWarnId().getServerId());
WarnEntry secondEntry = models.get(1);
Assert.assertEquals(warningMember, secondEntry.getWarningUser().getMember());
Assert.assertEquals(warnedMember, secondEntry.getWarnedUser().getMember());
Assert.assertEquals(USER_ID_1, secondEntry.getWarnedUser().getUserId());
Assert.assertEquals(USER_ID_2, secondEntry.getWarningUser().getUserId());
Assert.assertEquals(WARN_ID_2, secondEntry.getWarning().getWarnId().getId());
Assert.assertEquals(SERVER_ID, secondEntry.getWarning().getWarnId().getServerId());
}
}

View File

@@ -52,7 +52,7 @@ public class BanServiceBeanTest {
when(mockedGuild.ban(user, 0, REASON)).thenReturn(mockedAction);
MessageToSend mockedMessage = Mockito.mock(MessageToSend.class);
when(templateService.renderEmbedTemplate(eq(BanServiceBean.BAN_LOG_TEMPLATE), any(), eq(SERVER_ID))).thenReturn(mockedMessage);
testUnit.banMember(memberToBan, REASON, banningMember, message);
testUnit.banMemberWithNotification(memberToBan, REASON, banningMember, 0, message);
verify(mockedGuild, times(1)).ban(user, 0, REASON);
verify(postTargetService, times(1)).sendEmbedInPostTarget(mockedMessage, ModerationPostTarget.BAN_LOG, SERVER_ID);
}

View File

@@ -361,7 +361,7 @@ public class MuteServiceBeanTest {
public void testCompletelyUnMuteNotMutedUser() {
when(userBeingMuted.getUserReference()).thenReturn(user);
when(userBeingMuted.getServerReference()).thenReturn(server);
when(muteManagementService.getAllMutesOf(userBeingMuted)).thenReturn(Arrays.asList());
when(muteManagementService.getAllActiveMutesOf(userBeingMuted)).thenReturn(Arrays.asList());
testUnit.completelyUnMuteUser(userBeingMuted);
verify(muteManagementService, times(0)).saveMute(any(Mute.class));
}
@@ -370,7 +370,7 @@ public class MuteServiceBeanTest {
public void testCompletelyUnMuteNotScheduledMuteUser() {
when(userBeingMuted.getUserReference()).thenReturn(user);
when(userBeingMuted.getServerReference()).thenReturn(server);
when(muteManagementService.getAllMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute));
when(muteManagementService.getAllActiveMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute));
testUnit.completelyUnMuteUser(userBeingMuted);
verify(muteManagementService, times(1)).saveMute(any(Mute.class));
verify(schedulerService, times(0)).stopTrigger(anyString());
@@ -381,7 +381,7 @@ public class MuteServiceBeanTest {
when(mute.getTriggerKey()).thenReturn(TRIGGER);
when(userBeingMuted.getUserReference()).thenReturn(user);
when(userBeingMuted.getServerReference()).thenReturn(server);
when(muteManagementService.getAllMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute));
when(muteManagementService.getAllActiveMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute));
testUnit.completelyUnMuteUser(userBeingMuted);
verify(muteManagementService, times(1)).saveMute(any(Mute.class));
verify(schedulerService, times(1)).stopTrigger(TRIGGER);
@@ -392,7 +392,7 @@ public class MuteServiceBeanTest {
when(userBeingMuted.getUserReference()).thenReturn(user);
when(userBeingMuted.getServerReference()).thenReturn(server);
when(mute.getTriggerKey()).thenReturn(TRIGGER);
when(muteManagementService.getAllMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute));
when(muteManagementService.getAllActiveMutesOf(userBeingMuted)).thenReturn(Arrays.asList(mute));
when(userInServerManagementService.loadOrCreateUser(memberBeingMuted)).thenReturn(userBeingMuted);
testUnit.completelyUnMuteMember(memberBeingMuted);
verify(muteManagementService, times(1)).saveMute(any(Mute.class));

View File

@@ -29,6 +29,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import java.time.Instant;
import java.util.Arrays;
@@ -134,6 +135,9 @@ public class WarnServiceBeanTest {
@Mock
private DefaultConfigManagementService defaultConfigManagementService;
@Mock
private FeatureFlagService featureFlagService;
private static final String NOTIFICATION_TEXT = "text";
private static final String GUILD_NAME = "guild";
private static final Long SERVER_ID = 4L;
@@ -204,6 +208,7 @@ public class WarnServiceBeanTest {
public void testWarnFullUser() {
setupWarnContext();
setupMocksForWarning();
when(featureFlagService.getFeatureFlagValue(ModerationFeatureDefinition.INFRACTIONS, SERVER_ID)).thenReturn(false);
CompletableFuture<Void> future = testUnit.notifyAndLogFullUserWarning(context);
future.join();
Assert.assertFalse(future.isCompletedExceptionally());

View File

@@ -126,7 +126,7 @@ public class MuteManagementServiceBeanTest {
Mute mute = Mockito.mock(Mute.class);
Mute mute2 = Mockito.mock(Mute.class);
when(muteRepository.findAllByMutedUserAndMuteEndedFalseOrderByMuteId_IdDesc(userInAServer)).thenReturn(Arrays.asList(mute, mute2));
List<Mute> allMutesOf = testUnit.getAllMutesOf(userInAServer);
List<Mute> allMutesOf = testUnit.getAllActiveMutesOf(userInAServer);
Assert.assertEquals(2, allMutesOf.size());
Assert.assertEquals(mute, allMutesOf.get(0));
Assert.assertEquals(mute2, allMutesOf.get(1));

View File

@@ -97,7 +97,7 @@ public class WarnManagementServiceBeanTest {
public void testActiveWarnCountOfUser() {
Long count = 5L;
when(warnRepository.countByWarnedUserAndDecayedFalse(warnedUser)).thenReturn(count);
Long activeWarnsForUserCount = testUnit.getActiveWarnsForUser(warnedUser);
Long activeWarnsForUserCount = testUnit.getActiveWarnCountForUser(warnedUser);
Assert.assertEquals(count, activeWarnsForUserCount);
}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation</artifactId>
<version>1.3.8</version>
<version>1.3.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -0,0 +1,32 @@
package dev.sheldan.abstracto.moderation.config.feature;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.PostTargetEnum;
import dev.sheldan.abstracto.moderation.config.posttarget.InfractionPostTarget;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class InfractionFeatureConfig implements FeatureConfig {
public static final String INFRACTION_LEVELS = "infractionLevels";
public static final String INFRACTION_LEVEL_PREFIX = "infractionLevel";
@Override
public FeatureDefinition getFeature() {
return ModerationFeatureDefinition.INFRACTIONS;
}
@Override
public List<String> getRequiredSystemConfigKeys() {
return Arrays.asList(INFRACTION_LEVELS);
}
@Override
public List<PostTargetEnum> getRequiredPostTargets() {
return Arrays.asList(InfractionPostTarget.INFRACTION_NOTIFICATION);
}
}

View File

@@ -11,7 +11,8 @@ public enum ModerationFeatureDefinition implements FeatureDefinition {
AUTOMATIC_WARN_DECAY("warnDecay"),
USER_NOTES("userNotes"),
INVITE_FILTER("inviteFilter"),
REPORT_REACTIONS("reportReactions")
REPORT_REACTIONS("reportReactions"),
INFRACTIONS("infractions")
;
private final String key;

View File

@@ -15,6 +15,8 @@ import java.util.List;
@Component
public class WarningFeatureConfig implements FeatureConfig {
public static final String WARN_INFRACTION_POINTS = "warnInfractionPoints";
@Autowired
private WarningDecayFeatureConfig warningDecayFeatureConfig;

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.moderation.config.posttarget;
import dev.sheldan.abstracto.core.config.PostTargetEnum;
import lombok.Getter;
@Getter
public enum InfractionPostTarget implements PostTargetEnum {
INFRACTION_NOTIFICATION("infractionNotification");
private String key;
InfractionPostTarget(String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.FeatureAwareListener;
import dev.sheldan.abstracto.moderation.model.listener.InfractionLevelChangedEventModel;
public interface InfractionLevelChangedListener extends FeatureAwareListener<InfractionLevelChangedEventModel, DefaultListenerResult> {
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.FeatureAwareListener;
import dev.sheldan.abstracto.moderation.model.listener.WarningCreatedEventModel;
public interface WarningCreatedListener extends FeatureAwareListener<WarningCreatedEventModel, DefaultListenerResult> {
}

Some files were not shown because too many files have changed in this diff Show More