Compare commits

...

20 Commits

Author SHA1 Message Date
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
162 changed files with 1845 additions and 885 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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>

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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>

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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>

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

@@ -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;
@@ -38,7 +36,7 @@ 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;
@@ -52,9 +50,6 @@ public class Warnings extends AbstractConditionableCommand {
@Autowired
private PaginatorService paginatorService;
@Autowired
private EventWaiter eventWaiter;
@Autowired
private ServerManagementService serverManagementService;
@@ -86,22 +81,20 @@ public class Warnings extends AbstractConditionableCommand {
.thenApply(unused -> CommandResult.fromSuccess());
} else {
return warnEntryConverter.fromWarnings(warnsToDisplay).thenApply(warnEntries -> {
self.renderWarnings(commandContext, warnEntries);
return CommandResult.fromSuccess();
});
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

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

@@ -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

@@ -365,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

@@ -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

@@ -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

@@ -12,4 +12,5 @@
<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

@@ -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

@@ -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

@@ -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

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

View File

@@ -0,0 +1,23 @@
package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.Duration;
import java.time.Instant;
@Getter
@Setter
@Builder
public class MuteEntry {
private Long muteId;
private Long serverId;
private String reason;
private Instant muteDate;
private Boolean muteEnded;
private MemberDisplay mutedUser;
private MemberDisplay mutingUser;
private Duration muteDuration;
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import java.util.List;
@Getter
@Setter
@SuperBuilder
public class MutesModel extends SlimUserInitiatedServerContext {
private List<MuteEntry> mutes;
}

View File

@@ -1,15 +1,19 @@
package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.moderation.model.database.UserNote;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.Instant;
@Getter
@Setter
@Builder
public class NoteEntryModel {
private UserNote note;
private FullUserInServer fullUser;
private String note;
private Long noteId;
private Instant created;
private MemberDisplay member;
private Long serverId;
}

View File

@@ -1,11 +1,13 @@
package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.MemberDisplayModel;
import dev.sheldan.abstracto.moderation.model.database.Warning;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.Instant;
/**
* A single warning containing the full user instead of only the warning object when the warnings command is executed.
* The template is: "warnings_warn_entry"
@@ -14,16 +16,18 @@ import lombok.Setter;
@Setter
@Builder
public class WarnEntry {
/**
* The {@link Warning} of this entry
*/
private Warning warning;
private String reason;
private Long warnId;
private Long serverId;
private Boolean decayed;
private Instant warnDate;
private Instant decayDate;
/**
* The {@link MemberDisplayModel} containing information about the user being warned. The member property is null if the user left the server
*/
private MemberDisplayModel warnedUser;
private MemberDisplay warnedUser;
/**
* The {@link MemberDisplayModel} containing information about the user warning. The member property is null if the user left the server
*/
private MemberDisplayModel warningUser;
private MemberDisplay warningUser;
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.moderation.service.management;
import dev.sheldan.abstracto.core.models.AServerAChannelMessage;
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 net.dv8tion.jda.api.entities.Member;
@@ -75,5 +76,18 @@ public interface MuteManagementService {
* @param aUserInAServer The {@link AUserInAServer} to search the active mutes for
* @return A collection of {@link Mute} objects of the user which are active
*/
List<Mute> getAllActiveMutesOf(AUserInAServer aUserInAServer);
/**
* Retrieves all mutes of the given {@link AUserInAServer} in a collection
* @param aUserInAServer The {@link AUserInAServer} to search the mutes for
* @return A collection of {@link Mute} objects of the user
*/
List<Mute> getAllMutesOf(AUserInAServer aUserInAServer);
/**
* Retrieves all {@link Mute} from the given {@link AServer}
* @return All found mutes of this server
*/
List<Mute> getAllMutes(AServer server);
}

View File

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

View File

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

View File

@@ -28,8 +28,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
@Slf4j
@@ -106,11 +109,24 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener
.aUserInAServer(modMailMessage.getThreadReference().getUser())
.member(targetMember)
.build();
List<String> imageUrls = loadedMessage
.getAttachments()
.stream()
.filter(Message.Attachment::isImage)
.map(Message.Attachment::getProxyUrl)
.collect(Collectors.toList());
Map<String, String> otherAttachments = loadedMessage
.getAttachments()
.stream()
.filter(attachment -> !attachment.isImage())
.collect(Collectors.toMap(Message.Attachment::getFileName, Message.Attachment::getUrl));
ModMailModeratorReplyModel.ModMailModeratorReplyModelBuilder modMailModeratorReplyModelBuilder = ModMailModeratorReplyModel
.builder()
.text(newText)
.modMailThread(modMailMessage.getThreadReference())
.postedMessage(loadedMessage)
.attachedImageUrls(imageUrls)
.remainingAttachments(otherAttachments)
.anonymous(modMailMessage.getAnonymous())
.threadUser(fullThreadUser);
if(modMailMessage.getAnonymous()) {

View File

@@ -498,10 +498,23 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.map(CompletableFuture::join)
.filter(Objects::nonNull)
.collect(Collectors.toList());
List<String> imageUrls = messageFromUser
.getAttachments()
.stream()
.filter(Message.Attachment::isImage)
.map(Message.Attachment::getProxyUrl)
.collect(Collectors.toList());
Map<String, String> otherAttachments = messageFromUser
.getAttachments()
.stream()
.filter(attachment -> !attachment.isImage())
.collect(Collectors.toMap(Message.Attachment::getFileName, Message.Attachment::getUrl));
ModMailUserReplyModel modMailUserReplyModel = ModMailUserReplyModel
.builder()
.postedMessage(messageFromUser)
.member(member)
.attachedImageUrls(imageUrls)
.remainingAttachments(otherAttachments)
.subscribers(subscribers)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_user_message", modMailUserReplyModel, textChannel.getGuild().getIdLong());
@@ -568,11 +581,24 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.aUserInAServer(modMailThread.getUser())
.member(targetMember)
.build();
List<String> imageUrls = replyCommandMessage
.getAttachments()
.stream()
.filter(Message.Attachment::isImage)
.map(Message.Attachment::getProxyUrl)
.collect(Collectors.toList());
Map<String, String> otherAttachments = replyCommandMessage
.getAttachments()
.stream()
.filter(attachment -> !attachment.isImage())
.collect(Collectors.toMap(Message.Attachment::getFileName, Message.Attachment::getUrl));
ModMailModeratorReplyModel.ModMailModeratorReplyModelBuilder modMailModeratorReplyModelBuilder = ModMailModeratorReplyModel
.builder()
.text(text)
.modMailThread(modMailThread)
.postedMessage(replyCommandMessage)
.remainingAttachments(otherAttachments)
.attachedImageUrls(imageUrls)
.anonymous(anonymous)
.threadUser(fullThreadUser);
if(anonymous) {
@@ -798,7 +824,8 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
return modMailMessage.getCreatedMessageInChannel().equals(message.getIdLong());
}
})
.findFirst().orElseThrow(() -> new AbstractoRunTimeException("Could not find desired message in list of messages in thread. This should not happen, as we just retrieved them from the same place."));
.findFirst()
.orElseThrow(() -> new AbstractoRunTimeException("Could not find desired message in list of messages in thread. This should not happen, as we just retrieved them from the same place."));
User author = authors.getOrDefault(modmailMessage.getAuthor().getUserReference().getId(), message.getJDA().getSelfUser());
ModMailLoggedMessageModel modMailLoggedMessageModel =
ModMailLoggedMessageModel

View File

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

View File

@@ -8,6 +8,9 @@ import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import java.util.List;
import java.util.Map;
/**
* Model used to render the response by staff members to the DM channel with the user
*/
@@ -36,6 +39,8 @@ public class ModMailModeratorReplyModel {
* Whether or not the reply should be shown anonymous
*/
private Boolean anonymous;
private List<String> attachedImageUrls;
private Map<String, String> remainingAttachments;
/**
* The {@link ModMailThread} to reply to
*/

View File

@@ -7,6 +7,7 @@ import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import java.util.List;
import java.util.Map;
/**
* Model used to render the response by the user to the mod mail thread channel.
@@ -23,6 +24,8 @@ public class ModMailUserReplyModel {
* The {@link Message} which was posted, which contains all the possible information
*/
private Message postedMessage;
private List<String> attachedImageUrls;
private Map<String, String> remainingAttachments;
/**
* List of {@link Member} which are registered as subscribers for a particular mod mail thread and will be pinged
* when the user sends a new message

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto</groupId>
<artifactId>abstracto-application</artifactId>
<version>1.3.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>profanity-filter</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.9</version>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>profanity-filter</artifactId>
<version>1.3.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>remind</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>repost-detection</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.9</version>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>repost-detection</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>starboard</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

@@ -0,0 +1,88 @@
package dev.sheldan.abstracto.statistic.emote.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.config.FeatureDefinition;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
import dev.sheldan.abstracto.statistic.emote.config.EmoteTrackingModuleDefinition;
import dev.sheldan.abstracto.statistic.emote.model.EmoteStatsResultDisplay;
import dev.sheldan.abstracto.statistic.emote.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emote.service.UsedEmoteService;
import dev.sheldan.abstracto.statistic.emote.service.management.TrackedEmoteManagementService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.statistic.emote.command.EmoteStats.EMOTE_STATS_NO_STATS_AVAILABLE;
@Component
@Slf4j
public class EmoteStat extends AbstractConditionableCommand {
@Autowired
private UsedEmoteService usedEmoteService;
@Autowired
private ChannelService channelService;
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
public static final String EMOTE_STAT_RESPONSE = "emoteStat_response";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
// default is 1.1.1970
Instant statsSince = Instant.EPOCH;
TrackedEmote emote = (TrackedEmote) parameters.get(0);
TrackedEmote trackedEmote = trackedEmoteManagementService.loadByTrackedEmoteServer(emote.getTrackedEmoteId());
if(parameters.size() == 2) {
// subtract the given Duration from the current point in time, if there is any
Duration duration = (Duration) parameters.get(1);
statsSince = Instant.now().minus(duration);
}
EmoteStatsResultDisplay emoteStatsModel = usedEmoteService.getEmoteStatForEmote(trackedEmote, statsSince);
if(emoteStatsModel.getResult().getAmount() == null) {
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_NO_STATS_AVAILABLE, new Object(), commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STAT_RESPONSE, emoteStatsModel, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("trackedEmote").templated(true).type(TrackedEmote.class).build());
parameters.add(Parameter.builder().name("period").templated(true).optional(true).type(Duration.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("emoteStat")
.module(EmoteTrackingModuleDefinition.EMOTE_TRACKING)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return StatisticFeatureDefinition.EMOTE_TRACKING;
}
}

View File

@@ -40,13 +40,13 @@ public class ShowTrackedEmotes extends AbstractConditionableCommand {
@Autowired
private FeatureModeService featureModeService;
public static final String EMOTE_STATS_STATIC_RESPONSE = "showTrackedEmotes_static_response";
public static final String EMOTE_STATS_ANIMATED_RESPONSE = "showTrackedEmotes_animated_response";
public static final String EMOTE_STATS_EXTERNAL_ANIMATED_RESPONSE = "showTrackedEmotes_external_animated_response";
public static final String EMOTE_STATS_EXTERNAL_STATIC_RESPONSE = "showTrackedEmotes_external_static_response";
public static final String EMOTE_STATS_DELETED_STATIC_RESPONSE = "showTrackedEmotes_deleted_static_response";
public static final String EMOTE_STATS_DELETED_ANIMATED_RESPONSE = "showTrackedEmotes_deleted_animated_response";
public static final String EMOTE_STATS_NO_STATS_AVAILABLE = "showTrackedEmotes_no_emotes_available";
public static final String SHOW_TRACKED_EMOTES_STATIC_RESPONSE = "showTrackedEmotes_static_response";
public static final String SHOW_TRACKED_EMOTES_ANIMATED_RESPONSE = "showTrackedEmotes_animated_response";
public static final String SHOW_TRACKED_EMOTES_EXTERNAL_ANIMATED_RESPONSE = "showTrackedEmotes_external_animated_response";
public static final String SHOW_TRACKED_EMOTES_EXTERNAL_STATIC_RESPONSE = "showTrackedEmotes_external_static_response";
public static final String SHOW_TRACKED_EMOTES_DELETED_STATIC_RESPONSE = "showTrackedEmotes_deleted_static_response";
public static final String SHOW_TRACKED_EMOTES_DELETED_ANIMATED_RESPONSE = "showTrackedEmotes_deleted_animated_response";
public static final String SHOW_TRACKED_EMOTES_NO_STATS_AVAILABLE = "showTrackedEmotes_no_emotes_available";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
@@ -63,25 +63,25 @@ public class ShowTrackedEmotes extends AbstractConditionableCommand {
// only show the embed, if there are static tracked emotes
if(!trackedEmoteOverview.getStaticEmotes().isEmpty()) {
noTrackedEmotesAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_STATIC_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_STATIC_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
// only show the embed if there are animated tracked emotes
if(!trackedEmoteOverview.getAnimatedEmotes().isEmpty()) {
noTrackedEmotesAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_ANIMATED_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_ANIMATED_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
// only show the embed, if there are deleted static emotes
if(!trackedEmoteOverview.getDeletedStaticEmotes().isEmpty()) {
noTrackedEmotesAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_DELETED_STATIC_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_DELETED_STATIC_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
// only show the embed, if there are deleted animated emotes
if(!trackedEmoteOverview.getDeletedAnimatedEmotes().isEmpty()) {
noTrackedEmotesAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_DELETED_ANIMATED_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_DELETED_ANIMATED_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
boolean externalTrackingEnabled = featureModeService.featureModeActive(StatisticFeatureDefinition.EMOTE_TRACKING, commandContext.getGuild().getIdLong(), EmoteTrackingMode.EXTERNAL_EMOTES);
@@ -92,19 +92,19 @@ public class ShowTrackedEmotes extends AbstractConditionableCommand {
// only show the embed if there are external static emotes
if(!trackedEmoteOverview.getExternalStaticEmotes().isEmpty()) {
noTrackedEmotesAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_EXTERNAL_STATIC_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_EXTERNAL_STATIC_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
// only show the embed if there are external animated emotes
if(!trackedEmoteOverview.getExternalAnimatedEmotes().isEmpty()) {
noTrackedEmotesAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_EXTERNAL_ANIMATED_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_EXTERNAL_ANIMATED_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
}
// if there are no tracked emotes available, show an embed indicating so
if(noTrackedEmotesAvailable) {
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_NO_STATS_AVAILABLE, new Object(), commandContext.getChannel()));
messagePromises.addAll(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_NO_STATS_AVAILABLE, new Object(), commandContext.getChannel()));
}
return FutureUtils.toSingleFutureGeneric(messagePromises)
.thenApply(unused -> CommandResult.fromIgnored());

View File

@@ -36,18 +36,7 @@ public class EmoteStatsConverter {
Guild relevantGuild = guildService.getGuildById(resultList.get(0).getServerId());
EmoteStatsModel resultingModel = EmoteStatsModel.builder().build();
resultList.forEach(emoteStatsResult -> {
TrackedEmote trackedEmote = trackedEmoteManagementService.loadByEmoteId(emoteStatsResult.getEmoteId(), emoteStatsResult.getServerId());
Emote loadedEmote = null;
// if the emote should still exist, we try to load it
if(!trackedEmote.getExternal() && !trackedEmote.getDeleted()) {
loadedEmote = relevantGuild.getEmoteById(trackedEmote.getTrackedEmoteId().getId());
}
EmoteStatsResultDisplay display = EmoteStatsResultDisplay
.builder()
.emote(loadedEmote)
.result(emoteStatsResult)
.trackedEmote(trackedEmote)
.build();
EmoteStatsResultDisplay display = convertEmoteStatsResult(relevantGuild, emoteStatsResult);
if(display.getTrackedEmote().getAnimated()) {
resultingModel.getAnimatedEmotes().add(display);
} else {
@@ -56,4 +45,24 @@ public class EmoteStatsConverter {
});
return resultingModel;
}
public EmoteStatsResultDisplay convertEmoteStatsResultToDisplay(EmoteStatsResult emoteStatsResult) {
Guild relevantGuild = guildService.getGuildById(emoteStatsResult.getServerId());
return convertEmoteStatsResult(relevantGuild, emoteStatsResult);
}
private EmoteStatsResultDisplay convertEmoteStatsResult(Guild relevantGuild, EmoteStatsResult emoteStatsResult) {
TrackedEmote trackedEmote = trackedEmoteManagementService.loadByEmoteId(emoteStatsResult.getEmoteId(), emoteStatsResult.getServerId());
Emote loadedEmote = null;
// if the emote should still exist, we try to load it
if(!trackedEmote.getExternal() && !trackedEmote.getDeleted()) {
loadedEmote = relevantGuild.getEmoteById(trackedEmote.getTrackedEmoteId().getId());
}
return EmoteStatsResultDisplay
.builder()
.emote(loadedEmote)
.result(emoteStatsResult)
.trackedEmote(trackedEmote)
.build();
}
}

View File

@@ -63,6 +63,13 @@ public interface UsedEmoteRepository extends JpaRepository<UsedEmote, UsedEmoteD
"order by amount desc", nativeQuery = true)
List<EmoteStatsResult> getCurrentlyExistingEmoteStatsForServerSince(@Param("server_id") Long serverId, @Param("start_date") Instant since);
@Query(value = "select :tracked_emote_id as emoteId, :server_id as serverId, sum(us.amount) as amount " +
"from used_emote us " +
"where us.use_date >= date_trunc('day', cast(:start_date AS timestamp)) " +
"and us.server_id = :server_id " +
"and us.emote_id = :tracked_emote_id", nativeQuery = true)
EmoteStatsResult getEmoteStatForTrackedEmote(@Param("tracked_emote_id") Long trackedEmoteId, @Param("server_id") Long serverId, @Param("start_date") Instant since);
void deleteByEmoteId_EmoteIdAndEmoteId_ServerIdAndEmoteId_UseDateGreaterThan(Long emoteId, Long serverId, Instant timestamp);
List<UsedEmote> getByEmoteId_ServerIdAndEmoteId_UseDateGreaterThan(Long emoteId, Instant timestamp);

View File

@@ -4,6 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.statistic.emote.converter.EmoteStatsConverter;
import dev.sheldan.abstracto.statistic.emote.model.EmoteStatsModel;
import dev.sheldan.abstracto.statistic.emote.model.EmoteStatsResult;
import dev.sheldan.abstracto.statistic.emote.model.EmoteStatsResultDisplay;
import dev.sheldan.abstracto.statistic.emote.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emote.service.management.UsedEmoteManagementService;
import lombok.extern.slf4j.Slf4j;
@@ -48,6 +49,12 @@ public class UsedEmoteServiceBean implements UsedEmoteService {
return converter.fromEmoteStatsResults(emoteStatsResults);
}
@Override
public EmoteStatsResultDisplay getEmoteStatForEmote(TrackedEmote trackedEmote, Instant since) {
EmoteStatsResult emoteStatsResult = usedEmoteManagementService.loadEmoteStatForEmote(trackedEmote, since);
return converter.convertEmoteStatsResultToDisplay(emoteStatsResult);
}
@Override
public void purgeEmoteUsagesSince(TrackedEmote emote, Instant since) {
log.info("Purging emote {} in server {} since {}.", emote.getTrackedEmoteId().getId(), emote.getTrackedEmoteId().getServerId(), since);

View File

@@ -67,6 +67,11 @@ public class UsedEmoteManagementServiceBean implements UsedEmoteManagementServic
return usedEmoteRepository.getCurrentlyExistingEmoteStatsForServerSince(server.getId(), since);
}
@Override
public EmoteStatsResult loadEmoteStatForEmote(TrackedEmote trackedEmote, Instant since) {
return usedEmoteRepository.getEmoteStatForTrackedEmote(trackedEmote.getTrackedEmoteId().getId(), trackedEmote.getTrackedEmoteId().getServerId(), since);
}
@Override
public void purgeEmoteUsagesSince(TrackedEmote emote, Instant since) {
usedEmoteRepository.deleteByEmoteId_EmoteIdAndEmoteId_ServerIdAndEmoteId_UseDateGreaterThan(emote.getTrackedEmoteId().getId(), emote.getTrackedEmoteId().getServerId(), since);

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="statisticModule" value="(SELECT id FROM module WHERE name = 'statistic')"/>
<property name="emoteTrackingFeature" value="(SELECT id FROM feature WHERE key = 'emoteTracking')"/>
<changeSet author="Sheldan" id="emoteStat-command">
<insert tableName="command">
<column name="name" value="emoteStat"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
</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

@@ -7,4 +7,5 @@
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="1.0-statistic/collection.xml" relativeToChangelogFile="true"/>
<include file="1.3.10/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -51,7 +51,7 @@ public class ShowTrackedEmotesTest {
when(commandContext.getGuild().getIdLong()).thenReturn(SERVER_ID);
TrackedEmoteOverview overview = Mockito.mock(TrackedEmoteOverview.class);
when(featureModeService.featureModeActive(StatisticFeatureDefinition.EMOTE_TRACKING, SERVER_ID, EmoteTrackingMode.EXTERNAL_EMOTES)).thenReturn(false);
when(channelService.sendEmbedTemplateInTextChannelList(eq(EMOTE_STATS_NO_STATS_AVAILABLE), any(), eq(commandContext.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(eq(SHOW_TRACKED_EMOTES_NO_STATS_AVAILABLE), any(), eq(commandContext.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
when(trackedEmoteService.loadTrackedEmoteOverview(commandContext.getGuild(), false)).thenReturn(overview);
CompletableFuture<CommandResult> asyncResult = testUnit.executeAsync(commandContext);
CommandTestUtilities.checkSuccessfulCompletionAsync(asyncResult);
@@ -64,7 +64,7 @@ public class ShowTrackedEmotesTest {
when(commandContext.getGuild().getIdLong()).thenReturn(SERVER_ID);
TrackedEmoteOverview overview = Mockito.mock(TrackedEmoteOverview.class);
when(featureModeService.featureModeActive(StatisticFeatureDefinition.EMOTE_TRACKING, SERVER_ID, EmoteTrackingMode.EXTERNAL_EMOTES)).thenReturn(false);
when(channelService.sendEmbedTemplateInTextChannelList(eq(EMOTE_STATS_NO_STATS_AVAILABLE), any(), eq(commandContext.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(eq(SHOW_TRACKED_EMOTES_NO_STATS_AVAILABLE), any(), eq(commandContext.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
when(trackedEmoteService.loadTrackedEmoteOverview(commandContext.getGuild(), true)).thenReturn(overview);
CompletableFuture<CommandResult> asyncResult = testUnit.executeAsync(commandContext);
CommandTestUtilities.checkSuccessfulCompletionAsync(asyncResult);
@@ -78,7 +78,7 @@ public class ShowTrackedEmotesTest {
TrackedEmoteOverview overview = Mockito.mock(TrackedEmoteOverview.class);
AvailableTrackedEmote staticEmote = Mockito.mock(AvailableTrackedEmote.class);
when(featureModeService.featureModeActive(StatisticFeatureDefinition.EMOTE_TRACKING, SERVER_ID, EmoteTrackingMode.EXTERNAL_EMOTES)).thenReturn(false);
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(overview.getStaticEmotes()).thenReturn(Arrays.asList(staticEmote));
when(trackedEmoteService.loadTrackedEmoteOverview(commandContext.getGuild(), false)).thenReturn(overview);
CompletableFuture<CommandResult> asyncResult = testUnit.executeAsync(commandContext);
@@ -94,7 +94,7 @@ public class ShowTrackedEmotesTest {
when(featureModeService.featureModeActive(StatisticFeatureDefinition.EMOTE_TRACKING, SERVER_ID, EmoteTrackingMode.EXTERNAL_EMOTES)).thenReturn(false);
TrackedEmoteOverview overview = Mockito.mock(TrackedEmoteOverview.class);
AvailableTrackedEmote animatedEmote = Mockito.mock(AvailableTrackedEmote.class);
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(overview.getAnimatedEmotes()).thenReturn(Arrays.asList(animatedEmote));
when(trackedEmoteService.loadTrackedEmoteOverview(commandContext.getGuild(), false)).thenReturn(overview);
CompletableFuture<CommandResult> asyncResult = testUnit.executeAsync(commandContext);
@@ -110,7 +110,7 @@ public class ShowTrackedEmotesTest {
TrackedEmoteOverview overview = Mockito.mock(TrackedEmoteOverview.class);
when(featureModeService.featureModeActive(StatisticFeatureDefinition.EMOTE_TRACKING, SERVER_ID, EmoteTrackingMode.EXTERNAL_EMOTES)).thenReturn(false);
TrackedEmote animatedEmote = Mockito.mock(TrackedEmote.class);
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_DELETED_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_DELETED_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(overview.getDeletedStaticEmotes()).thenReturn(Arrays.asList(animatedEmote));
when(trackedEmoteService.loadTrackedEmoteOverview(commandContext.getGuild(), false)).thenReturn(overview);
CompletableFuture<CommandResult> asyncResult = testUnit.executeAsync(commandContext);
@@ -126,7 +126,7 @@ public class ShowTrackedEmotesTest {
TrackedEmoteOverview overview = Mockito.mock(TrackedEmoteOverview.class);
when(featureModeService.featureModeActive(StatisticFeatureDefinition.EMOTE_TRACKING, SERVER_ID, EmoteTrackingMode.EXTERNAL_EMOTES)).thenReturn(false);
TrackedEmote animatedEmote = Mockito.mock(TrackedEmote.class);
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_DELETED_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_DELETED_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(overview.getDeletedAnimatedEmotes()).thenReturn(Arrays.asList(animatedEmote));
when(trackedEmoteService.loadTrackedEmoteOverview(commandContext.getGuild(), false)).thenReturn(overview);
CompletableFuture<CommandResult> asyncResult = testUnit.executeAsync(commandContext);
@@ -143,7 +143,7 @@ public class ShowTrackedEmotesTest {
TrackedEmoteOverview overview = Mockito.mock(TrackedEmoteOverview.class);
when(featureModeService.featureModeActive(StatisticFeatureDefinition.EMOTE_TRACKING, SERVER_ID, EmoteTrackingMode.EXTERNAL_EMOTES)).thenReturn(true);
TrackedEmote animatedEmote = Mockito.mock(TrackedEmote.class);
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_EXTERNAL_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_EXTERNAL_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(overview.getExternalStaticEmotes()).thenReturn(Arrays.asList(animatedEmote));
when(trackedEmoteService.loadTrackedEmoteOverview(commandContext.getGuild(), false)).thenReturn(overview);
CompletableFuture<CommandResult> asyncResult = testUnit.executeAsync(commandContext);
@@ -159,7 +159,7 @@ public class ShowTrackedEmotesTest {
TrackedEmoteOverview overview = Mockito.mock(TrackedEmoteOverview.class);
when(featureModeService.featureModeActive(StatisticFeatureDefinition.EMOTE_TRACKING, SERVER_ID, EmoteTrackingMode.EXTERNAL_EMOTES)).thenReturn(true);
TrackedEmote animatedEmote = Mockito.mock(TrackedEmote.class);
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_EXTERNAL_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_EXTERNAL_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(overview.getExternalAnimatedEmotes()).thenReturn(Arrays.asList(animatedEmote));
when(trackedEmoteService.loadTrackedEmoteOverview(commandContext.getGuild(), false)).thenReturn(overview);
CompletableFuture<CommandResult> asyncResult = testUnit.executeAsync(commandContext);
@@ -175,12 +175,12 @@ public class ShowTrackedEmotesTest {
TrackedEmoteOverview overview = Mockito.mock(TrackedEmoteOverview.class);
when(featureModeService.featureModeActive(StatisticFeatureDefinition.EMOTE_TRACKING, SERVER_ID, EmoteTrackingMode.EXTERNAL_EMOTES)).thenReturn(true);
TrackedEmote animatedEmote = Mockito.mock(TrackedEmote.class);
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_DELETED_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_DELETED_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_EXTERNAL_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(EMOTE_STATS_EXTERNAL_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_DELETED_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_DELETED_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_EXTERNAL_STATIC_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(channelService.sendEmbedTemplateInTextChannelList(SHOW_TRACKED_EMOTES_EXTERNAL_ANIMATED_RESPONSE, overview, commandContext.getChannel())).thenReturn(CommandTestUtilities.messageFutureList());
when(overview.getExternalAnimatedEmotes()).thenReturn(Arrays.asList(animatedEmote));
when(overview.getExternalStaticEmotes()).thenReturn(Arrays.asList(animatedEmote));
AvailableTrackedEmote trackedEmote = Mockito.mock(AvailableTrackedEmote.class);
@@ -198,25 +198,25 @@ public class ShowTrackedEmotesTest {
private void verifyNoMessage(CommandContext commandContext, boolean noStats, boolean staticEmote, boolean animatedEmote, boolean deletedStatic, boolean deletedAnimated, boolean externalStatic, boolean externalAnimated) {
if(noStats) {
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(EMOTE_STATS_NO_STATS_AVAILABLE), any(), eq(commandContext.getChannel()));
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(SHOW_TRACKED_EMOTES_NO_STATS_AVAILABLE), any(), eq(commandContext.getChannel()));
}
if(staticEmote) {
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(EMOTE_STATS_STATIC_RESPONSE), any(), eq(commandContext.getChannel()));
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(SHOW_TRACKED_EMOTES_STATIC_RESPONSE), any(), eq(commandContext.getChannel()));
}
if(animatedEmote) {
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(EMOTE_STATS_ANIMATED_RESPONSE), any(), eq(commandContext.getChannel()));
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(SHOW_TRACKED_EMOTES_ANIMATED_RESPONSE), any(), eq(commandContext.getChannel()));
}
if(deletedStatic) {
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(EMOTE_STATS_DELETED_STATIC_RESPONSE), any(), eq(commandContext.getChannel()));
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(SHOW_TRACKED_EMOTES_DELETED_STATIC_RESPONSE), any(), eq(commandContext.getChannel()));
}
if(deletedAnimated) {
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(EMOTE_STATS_DELETED_ANIMATED_RESPONSE), any(), eq(commandContext.getChannel()));
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(SHOW_TRACKED_EMOTES_DELETED_ANIMATED_RESPONSE), any(), eq(commandContext.getChannel()));
}
if(externalStatic) {
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(EMOTE_STATS_EXTERNAL_STATIC_RESPONSE), any(), eq(commandContext.getChannel()));
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(SHOW_TRACKED_EMOTES_EXTERNAL_STATIC_RESPONSE), any(), eq(commandContext.getChannel()));
}
if(externalAnimated) {
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(EMOTE_STATS_EXTERNAL_ANIMATED_RESPONSE), any(), eq(commandContext.getChannel()));
verify(channelService, times(0)).sendEmbedTemplateInTextChannelList(eq(SHOW_TRACKED_EMOTES_EXTERNAL_ANIMATED_RESPONSE), any(), eq(commandContext.getChannel()));
}
}

View File

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

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.statistic.emote.service;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.statistic.emote.model.EmoteStatsModel;
import dev.sheldan.abstracto.statistic.emote.model.EmoteStatsResultDisplay;
import dev.sheldan.abstracto.statistic.emote.model.database.TrackedEmote;
import java.time.Instant;
@@ -46,6 +47,7 @@ public interface UsedEmoteService {
* @return An {@link EmoteStatsModel} containing the statistics split by animated and static {@link net.dv8tion.jda.api.entities.Emote}
*/
EmoteStatsModel getActiveEmoteStatsForServerSince(AServer server, Instant since);
EmoteStatsResultDisplay getEmoteStatForEmote(TrackedEmote trackedEmote, Instant since);
/**
* Removes all {@link dev.sheldan.abstracto.statistic.emote.model.database.UsedEmote} for the given {@link TrackedEmote} which are younger

View File

@@ -77,6 +77,7 @@ public interface UsedEmoteManagementService {
* @return A list of {@link EmoteStatsResult} from the {@link AServer} newer than the given {@link Instant} for all active {@link TrackedEmote}
*/
List<EmoteStatsResult> loadActiveEmoteStatsForServerSince(AServer server, Instant since);
EmoteStatsResult loadEmoteStatForEmote(TrackedEmote trackedEmote, Instant since);
/**
* Deletes all emote usages for the {@link TrackedEmote} which are younger than the given {@link Instant}

View File

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

View File

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

View File

@@ -58,7 +58,7 @@ public class SuggestionButtonVoteClickedListener implements ButtonClickedListene
@Override
public Boolean handlesEvent(ButtonClickedListenerModel model) {
return model.getOrigin().equals(SuggestionServiceBean.SUGGESTION_VOTE_ORIGIN);
return SuggestionServiceBean.SUGGESTION_VOTE_ORIGIN.equals(model.getOrigin());
}
@Override

View File

@@ -402,6 +402,7 @@ public class SuggestionServiceBean implements SuggestionService {
public void deleteSuggestion(Long suggestionId, Long serverId) {
Suggestion suggestion = suggestionManagementService.getSuggestion(serverId, suggestionId);
cancelSuggestionReminder(suggestion);
suggestionVoteManagementService.deleteSuggestionVotes(suggestion);
suggestionManagementService.deleteSuggestion(suggestion);
}
}

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>suggestion</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.9</version>
<version>1.3.11</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.9</version>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

@@ -26,6 +26,7 @@ import java.util.concurrent.CompletableFuture;
@Slf4j
public class ServerInfo extends AbstractConditionableCommand {
public static final String SERVERINFO_RESPONSE_TEMPLATE_KEY = "serverinfo_response";
@Autowired
private ChannelService channelService;
@@ -34,13 +35,14 @@ public class ServerInfo extends AbstractConditionableCommand {
log.info("Displaying serverinfo for server {}", commandContext.getGuild().getId());
ServerInfoModel model = buildModel(commandContext);
return FutureUtils.toSingleFutureGeneric(
channelService.sendEmbedTemplateInTextChannelList("serverinfo_response", model, commandContext.getChannel()))
channelService.sendEmbedTemplateInTextChannelList(SERVERINFO_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
}
private ServerInfoModel buildModel(CommandContext commandContext) {
ServerInfoModel model = (ServerInfoModel) ContextConverter.fromCommandContext(commandContext, ServerInfoModel.class);
model.setGuild(commandContext.getGuild());
model.setTimeCreated(commandContext.getGuild().getTimeCreated().toInstant());
List<EmoteDisplay> staticEmotes = new ArrayList<>();
List<EmoteDisplay> animatedEmotes = new ArrayList<>();
commandContext.getGuild().getEmotes().forEach(emote -> {

View File

@@ -12,6 +12,9 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@@ -28,6 +31,7 @@ public class ServerInfoTest {
@Test
public void executeCommand() {
CommandContext context = CommandTestUtilities.getNoParameters();
when(context.getGuild().getTimeCreated()).thenReturn(OffsetDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()));
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
verify(channelService, times(1)).sendEmbedTemplateInTextChannelList(eq("serverinfo_response"), any(ServerInfoModel.class), eq(context.getChannel()));
CommandTestUtilities.checkSuccessfulCompletionAsync(result);

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