[AB-358] upgrading to JDA 5

removal of jda-utils
adding message context commands
[AB-360] fixing confirmation buttons being triggered by somebody not the author
This commit is contained in:
Sheldan
2022-02-27 23:51:06 +01:00
parent 17470f9718
commit 09450429dd
248 changed files with 2362 additions and 1482 deletions

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -31,8 +31,8 @@ import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.events.interaction.ButtonClickEvent;
import net.dv8tion.jda.api.interactions.components.ButtonInteraction;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.interactions.components.buttons.ButtonInteraction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -74,7 +74,7 @@ public class AssignableRoleButtonClickedListener implements ButtonClickedListene
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
ButtonClickEvent event = model.getEvent();
ButtonInteractionEvent event = model.getEvent();
Member member = event.getMember();
if(event.getGuild() != null && member != null) {
AssignableRolePlacePayload payload = (AssignableRolePlacePayload) model.getDeserializedPayload();
@@ -219,7 +219,7 @@ public class AssignableRoleButtonClickedListener implements ButtonClickedListene
@Override
public Boolean handlesEvent(ButtonClickedListenerModel model) {
return AssignableRolePlaceServiceBean.ASSIGNABLE_ROLE_COMPONENT_ORIGIN.equals(model.getOrigin());
return AssignableRolePlaceServiceBean.ASSIGNABLE_ROLE_COMPONENT_ORIGIN.equals(model.getOrigin()) && model.getEvent().isFromGuild();
}
@Override

View File

@@ -24,7 +24,7 @@ import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.interactions.components.ButtonStyle;
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -118,9 +118,9 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
throw new EmoteNotUsableException(fakeEmote.getEmote());
}
}
Optional<TextChannel> channelOptional = channelService.getTextChannelFromServerOptional(server.getId(), assignableRolePlace.getChannel().getId());
Optional<GuildMessageChannel> channelOptional = channelService.getMessageChannelFromServerOptional(server.getId(), assignableRolePlace.getChannel().getId());
if (channelOptional.isPresent()) {
TextChannel textChannel = channelOptional.get();
GuildMessageChannel textChannel = channelOptional.get();
String buttonId = componentService.generateComponentId();
String emoteMarkdown = fakeEmote != null ? fakeEmote.getEmoteRepr() : null;
if (assignableRolePlace.getMessageId() != null) {
@@ -223,7 +223,7 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
AssignablePostMessage model = prepareAssignablePostMessageModel(place);
MessageToSend messageToSend = templateService.renderEmbedTemplate(ASSIGNABLE_ROLES_POST_TEMPLATE_KEY, model, place.getServer().getId());
Long channelId = place.getChannel().getId();
Optional<TextChannel> channelOptional = channelService.getTextChannelFromServerOptional(place.getServer().getId(), channelId);
Optional<GuildMessageChannel> channelOptional = channelService.getMessageChannelFromServerOptional(place.getServer().getId(), channelId);
if (channelOptional.isPresent()) {
log.info("Refreshing text for assignable role place {} in channel {} in post {}.", place.getId(), channelId, place.getMessageId());
return channelService.editEmbedMessageInAChannel(messageToSend.getEmbeds().get(0), channelOptional.get(), place.getMessageId()).thenCompose(message -> CompletableFuture.completedFuture(null));
@@ -435,7 +435,7 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
.build();
}
private CompletableFuture<Void> sendAssignablePostMessage(AssignableRolePlace place, TextChannel channel) {
private CompletableFuture<Void> sendAssignablePostMessage(AssignableRolePlace place, GuildMessageChannel channel) {
AssignablePostMessage model = prepareAssignablePostMessageModel(place);
MessageToSend messageToSend = templateService.renderEmbedTemplate(ASSIGNABLE_ROLES_POST_TEMPLATE_KEY, model, place.getServer().getId());
log.info("Sending message for assignable role place {}.", place.getId());
@@ -501,9 +501,9 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
@Transactional
public CompletableFuture<Void> createAssignableRolePlacePost(Long serverId, Long assignablePlaceId) {
AssignableRolePlace assignableRolePlace = rolePlaceManagementService.findByPlaceId(assignablePlaceId);
Optional<TextChannel> channelOptional = channelService.getTextChannelFromServerOptional(serverId, assignableRolePlace.getChannel().getId());
Optional<GuildMessageChannel> channelOptional = channelService.getMessageChannelFromServerOptional(serverId, assignableRolePlace.getChannel().getId());
if (channelOptional.isPresent()) {
TextChannel channel = channelOptional.get();
GuildMessageChannel channel = channelOptional.get();
log.info("Sending assignable role place posts for place {} in channel {} in server {}.", assignableRolePlace.getId(), channel.getId(), serverId);
return sendAssignablePostMessage(assignableRolePlace, channel);
} else {

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>assignable-roles</artifactId>
<version>1.3.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -16,7 +16,7 @@ import dev.sheldan.abstracto.linkembed.service.MessageEmbedMetricService;
import dev.sheldan.abstracto.linkembed.service.MessageEmbedServiceBean;
import dev.sheldan.abstracto.linkembed.service.management.MessageEmbedPostManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.interaction.ButtonClickEvent;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -51,7 +51,7 @@ public class MessageEmbedDeleteButtonClickedListener implements ButtonClickedLis
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
ButtonClickEvent event = model.getEvent();
ButtonInteractionEvent event = model.getEvent();
MessageEmbedDeleteButtonPayload payload = (MessageEmbedDeleteButtonPayload) model.getDeserializedPayload();
Long clickingUserId = event.getInteraction().getUser().getIdLong();
boolean embeddedUserRemoves = clickingUserId.equals(payload.getEmbeddedUserId());
@@ -77,7 +77,7 @@ public class MessageEmbedDeleteButtonClickedListener implements ButtonClickedLis
@Override
public Boolean handlesEvent(ButtonClickedListenerModel model) {
return MessageEmbedServiceBean.MESSAGE_EMBED_DELETE_ORIGIN.equals(model.getOrigin());
return MessageEmbedServiceBean.MESSAGE_EMBED_DELETE_ORIGIN.equals(model.getOrigin()) && model.getEvent().isFromGuild();
}
@Override

View File

@@ -7,6 +7,7 @@ import dev.sheldan.abstracto.core.listener.sync.jda.MessageReceivedListener;
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.GuildMemberMessageChannel;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.MessageCache;
@@ -97,9 +98,16 @@ public class MessageEmbedListener implements MessageReceivedListener {
@Transactional
public void embedSingleLink(Message message, Long cause, CachedMessage cachedMessage) {
GuildMemberMessageChannel context = GuildMemberMessageChannel
.builder()
.guildChannel(message.getGuildChannel())
.member(message.getMember())
.guild(message.getGuild())
.message(message)
.build();
log.info("Embedding link to message {} in channel {} in server {} to channel {} and server {}.",
cachedMessage.getMessageId(), cachedMessage.getChannelId(), cachedMessage.getServerId(), message.getChannel().getId(), message.getGuild().getId());
messageEmbedService.embedLink(cachedMessage, message.getTextChannel(), cause , message).thenAccept(unused ->
messageEmbedService.embedLink(cachedMessage, message.getGuildChannel(), cause , context).thenAccept(unused ->
metricService.incrementCounter(MESSAGE_EMBED_CREATED)
).exceptionally(throwable -> {
log.error("Failed to embed link towards message {} in channel {} in sever {} linked from message {} in channel {} in server {}.", cachedMessage.getMessageId(), cachedMessage.getChannelId(), cachedMessage.getServerId(),

View File

@@ -0,0 +1,83 @@
package dev.sheldan.abstracto.linkembed.listener.interaction;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.MessageContextConfig;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.MessageContextCommandListener;
import dev.sheldan.abstracto.core.models.GuildMemberMessageChannel;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.interaction.MessageContextInteractionModel;
import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.linkembed.config.LinkEmbedFeatureDefinition;
import dev.sheldan.abstracto.linkembed.service.MessageEmbedService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.Command;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
@Slf4j
public class MessageEmbedContextCommandListener implements MessageContextCommandListener {
@Autowired
private MessageCache messageCache;
@Autowired
private MessageEmbedService messageEmbedService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private MessageEmbedContextCommandListener self;
@Override
public DefaultListenerResult execute(MessageContextInteractionModel model) {
MessageContextInteractionEvent event = model.getEvent();
event.deferReply().queue();
Message targetMessage = event.getInteraction().getTarget();
Member actor = model.getEvent().getMember();
messageCache.getMessageFromCache(targetMessage)
.thenAccept(cachedMessage -> self.embedMessage(model, actor, cachedMessage));
return DefaultListenerResult.PROCESSED;
}
@Transactional
public void embedMessage(MessageContextInteractionModel model, Member actor, CachedMessage cachedMessage) {
Long userEmbeddingUserInServerId = userInServerManagementService.loadOrCreateUser(actor).getUserInServerId();
GuildMemberMessageChannel context = GuildMemberMessageChannel
.builder()
.message(null)
.guild(actor.getGuild())
.member(actor)
.guildChannel(model.getEvent().getGuildChannel())
.build();
messageEmbedService.embedLink(cachedMessage, model.getEvent().getGuildChannel(), userEmbeddingUserInServerId, context, model.getEvent().getInteraction());
}
@Override
public FeatureDefinition getFeature() {
return LinkEmbedFeatureDefinition.LINK_EMBEDS;
}
@Override
public MessageContextConfig getConfig() {
return MessageContextConfig
.builder()
.name("Embed message")
.build();
}
@Override
public Boolean handlesEvent(MessageContextInteractionModel model) {
return model.getEvent().getName().equals(getConfig().getName())
&& model.getEvent().isFromGuild()
&& model.getEvent().getCommandType().equals(Command.Type.MESSAGE);
}
}

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.linkembed.service;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.GuildMemberMessageChannel;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AServer;
@@ -22,9 +24,11 @@ import dev.sheldan.abstracto.linkembed.model.MessageEmbedLink;
import dev.sheldan.abstracto.linkembed.model.database.EmbeddedMessage;
import dev.sheldan.abstracto.linkembed.service.management.MessageEmbedPostManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.interactions.commands.CommandInteraction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@@ -48,18 +52,12 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
public static final String MESSAGE_EMBED_TEMPLATE = "message_embed";
public static final String REMOVAL_EMOTE = "removeEmbed";
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private MemberService memberService;
@Autowired
private UserService userService;
@@ -84,15 +82,15 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
@Autowired
private MessageService messageService;
@Autowired
private EmoteService emoteService;
@Autowired
private ComponentService componentServiceBean;
@Autowired
private FeatureModeService featureModeService;
@Autowired
private InteractionService interactionService;
@Autowired
private ComponentPayloadManagementService componentPayloadManagementService;
@@ -126,10 +124,10 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
}
@Override
public void embedLinks(List<MessageEmbedLink> linksToEmbed, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage) {
public void embedLinks(List<MessageEmbedLink> linksToEmbed, GuildMessageChannel target, Long userEmbeddingUserInServerId, GuildMemberMessageChannel executionContext) {
linksToEmbed.forEach(messageEmbedLink ->
messageCache.getMessageFromCache(messageEmbedLink.getServerId(), messageEmbedLink.getChannelId(), messageEmbedLink.getMessageId())
.thenAccept(cachedMessage -> self.embedLink(cachedMessage, target, userEmbeddingUserInServerId, embeddingMessage)
.thenAccept(cachedMessage -> self.embedLink(cachedMessage, target, userEmbeddingUserInServerId, executionContext)
).exceptionally(throwable -> {
log.error("Message embedding from cache failed for message {}.", messageEmbedLink.getMessageId(), throwable);
return null;
@@ -139,13 +137,21 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
@Override
@Transactional
public CompletableFuture<Void> embedLink(CachedMessage cachedMessage, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage) {
public CompletableFuture<Void> embedLink(CachedMessage cachedMessage, GuildMessageChannel target, Long userEmbeddingUserInServerId, GuildMemberMessageChannel executionContext) {
boolean deletionButtonEnabled = featureModeService.featureModeActive(LinkEmbedFeatureDefinition.LINK_EMBEDS, target.getGuild(), LinkEmbedFeatureMode.DELETE_BUTTON);
return buildTemplateParameter(embeddingMessage, cachedMessage, deletionButtonEnabled).thenCompose(messageEmbeddedModel ->
return buildTemplateParameter(executionContext, cachedMessage, deletionButtonEnabled).thenCompose(messageEmbeddedModel ->
self.sendEmbeddingMessage(cachedMessage, target, userEmbeddingUserInServerId, messageEmbeddedModel, deletionButtonEnabled)
);
}
@Override
public CompletableFuture<Void> embedLink(CachedMessage cachedMessage, GuildMessageChannel target, Long userEmbeddingUserInServerId, GuildMemberMessageChannel executionContext, CommandInteraction interaction) {
boolean deletionButtonEnabled = featureModeService.featureModeActive(LinkEmbedFeatureDefinition.LINK_EMBEDS, target.getGuild(), LinkEmbedFeatureMode.DELETE_BUTTON);
return buildTemplateParameter(executionContext, cachedMessage, deletionButtonEnabled).thenCompose(messageEmbeddedModel ->
self.sendEmbeddingMessageToInteraction(cachedMessage, target, userEmbeddingUserInServerId, messageEmbeddedModel, deletionButtonEnabled, interaction)
);
}
@Override
public CompletableFuture<Void> cleanUpOldMessageEmbeds() {
Instant oldestDate = Instant.now().truncatedTo(ChronoUnit.DAYS).minus(embedRemovalDays, ChronoUnit.DAYS);
@@ -243,11 +249,15 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
}
@Transactional
public CompletableFuture<Void> sendEmbeddingMessage(CachedMessage cachedMessage, TextChannel target,
public CompletableFuture<Void> sendEmbeddingMessage(CachedMessage cachedMessage, GuildMessageChannel target,
Long userEmbeddingUserInServerId, MessageEmbeddedModel messageEmbeddedModel, Boolean deletionButtonEnabled) {
MessageToSend embed = templateService.renderEmbedTemplate(MESSAGE_EMBED_TEMPLATE, messageEmbeddedModel, target.getGuild().getIdLong());
AUserInAServer cause = userInServerManagementService.loadOrCreateUser(userEmbeddingUserInServerId);
List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToSendToChannel(embed, target);
return postProcessLinkEmbed(cachedMessage, userEmbeddingUserInServerId, messageEmbeddedModel, deletionButtonEnabled, completableFutures);
}
private CompletableFuture<Void> postProcessLinkEmbed(CachedMessage cachedMessage, Long userEmbeddingUserInServerId, MessageEmbeddedModel messageEmbeddedModel, Boolean deletionButtonEnabled, List<CompletableFuture<Message>> completableFutures) {
AUserInAServer cause = userInServerManagementService.loadOrCreateUser(userEmbeddingUserInServerId);
Long embeddingUserId = cause.getUserReference().getId();
log.debug("Embedding message {} from channel {} from server {}, because of user {}", cachedMessage.getMessageId(),
cachedMessage.getChannelId(), cachedMessage.getServerId(), embeddingUserId);
@@ -257,6 +267,14 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
});
}
@Transactional
public CompletableFuture<Void> sendEmbeddingMessageToInteraction(CachedMessage cachedMessage, GuildMessageChannel target,
Long userEmbeddingUserInServerId, MessageEmbeddedModel messageEmbeddedModel, Boolean deletionButtonEnabled, CommandInteraction interaction) {
MessageToSend embed = templateService.renderEmbedTemplate(MESSAGE_EMBED_TEMPLATE, messageEmbeddedModel, target.getGuild().getIdLong());
List<CompletableFuture<Message>> completableFutures = interactionService.sendMessageToInteraction(embed, interaction.getHook());
return postProcessLinkEmbed(cachedMessage, userEmbeddingUserInServerId, messageEmbeddedModel, deletionButtonEnabled, completableFutures);
}
@Transactional
public CompletableFuture<Void> addDeletionPossibility(CachedMessage cachedMessage, MessageEmbeddedModel messageEmbeddedModel,
Message createdMessage, Long embeddingUserId, Boolean deletionButtonEnabled) {
@@ -299,34 +317,37 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
messageEmbedPostManagementService.createMessageEmbed(cachedMessage, createdMessage, innerCause, deletionButtonId);
}
private CompletableFuture<MessageEmbeddedModel> buildTemplateParameter(Message message, CachedMessage embeddedMessage, Boolean deletionButtonEnabled) {
private CompletableFuture<MessageEmbeddedModel> buildTemplateParameter(GuildMemberMessageChannel executionContext, CachedMessage embeddedMessage, Boolean deletionButtonEnabled) {
return userService.retrieveUserForId(embeddedMessage.getAuthor().getAuthorId()).thenApply(authorUser ->
self.loadMessageEmbedModel(message, embeddedMessage, authorUser, deletionButtonEnabled)
self.loadMessageEmbedModel(executionContext, embeddedMessage, authorUser, deletionButtonEnabled)
).exceptionally(throwable -> {
log.warn("Failed to retrieve author for user {}.", embeddedMessage.getAuthor().getAuthorId(), throwable);
return self.loadMessageEmbedModel(message, embeddedMessage, null, deletionButtonEnabled);
return self.loadMessageEmbedModel(executionContext, embeddedMessage, null, deletionButtonEnabled);
});
}
@Transactional
public MessageEmbeddedModel loadMessageEmbedModel(Message message, CachedMessage embeddedMessage, User userAuthor, Boolean deletionButtonEnabled) {
Optional<TextChannel> textChannelFromServer = channelService.getTextChannelFromServerOptional(embeddedMessage.getServerId(), embeddedMessage.getChannelId());
TextChannel sourceChannel = textChannelFromServer.orElse(null);
public MessageEmbeddedModel loadMessageEmbedModel(GuildMemberMessageChannel executionContext, CachedMessage embeddedMessage, User userAuthor, Boolean deletionButtonEnabled) {
Optional<GuildMessageChannel> textChannelFromServer = channelService.getMessageChannelFromServerOptional(embeddedMessage.getServerId(), embeddedMessage.getChannelId());
GuildMessageChannel sourceChannel = textChannelFromServer.orElse(null);
ButtonConfigModel buttonConfigModel = ButtonConfigModel
.builder()
.buttonId(deletionButtonEnabled ? componentServiceBean.generateComponentId() : null)
.build();
Long referencedMessageId = message.getReferencedMessage() != null ? message.getReferencedMessage().getIdLong() : null;
Boolean shouldMentionReferencedAuthor = shouldMentionReferencedAuthor(message);
Long referencedMessageId = null;
Boolean shouldMentionReferencedAuthor = false;
if(executionContext.getMessage() != null) {
referencedMessageId = executionContext.getMessage().getReferencedMessage() != null ? executionContext.getMessage().getReferencedMessage().getIdLong() : null;
shouldMentionReferencedAuthor = shouldMentionReferencedAuthor(executionContext.getMessage());
}
return MessageEmbeddedModel
.builder()
.member(message.getMember())
.member(executionContext.getMember())
.author(userAuthor)
.sourceChannel(sourceChannel)
.embeddingUser(message.getMember())
.messageChannel(message.getChannel())
.guild(message.getGuild())
.embeddingUser(executionContext.getMember())
.messageChannel(executionContext.getGuildChannel())
.guild(executionContext.getGuild())
.useButton(deletionButtonEnabled)
.embeddedMessage(embeddedMessage)
.referencedMessageId(referencedMessageId)

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.linkembed.listener;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.models.GuildMemberMessageChannel;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
@@ -49,11 +50,14 @@ public class MessageEmbedListenerTest {
@Mock
private MessageEmbedListener self;
@Mock
private GuildMemberMessageChannel context;
@Mock
private Message message;
@Mock
private TextChannel textChannel;
private GuildMessageChannel textChannel;
@Mock
private MessageReceivedModel model;
@@ -71,8 +75,8 @@ public class MessageEmbedListenerTest {
@Before
public void setup(){
when(guild.getIdLong()).thenReturn(FIRST_SERVER_ID);
when(message.getGuild()).thenReturn(guild);
when(message.getChannel()).thenReturn(textChannel);
when(context.getGuild()).thenReturn(guild);
when(context.getGuildChannel()).thenReturn(textChannel);
}
@Test
@@ -82,7 +86,6 @@ public class MessageEmbedListenerTest {
setupMessageConfig();
List<MessageEmbedLink> foundMessageLinks = new ArrayList<>();
when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks);
when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
verify(messageService, times(0)).deleteMessage(message);
}
@@ -124,7 +127,7 @@ public class MessageEmbedListenerTest {
when(foundLink.getServerId()).thenReturn(SECOND_SERVER_ID);
List<MessageEmbedLink> foundMessageLinks = Arrays.asList(foundLink);
Member author = Mockito.mock(Member.class);
when(message.getMember()).thenReturn(author);
when(context.getMember()).thenReturn(author);
when(userInServerManagementService.loadOrCreateUser(author)).thenReturn(userInAServer);
when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks);
when(model.getMessage()).thenReturn(message);
@@ -153,8 +156,8 @@ public class MessageEmbedListenerTest {
when(message.getContentRaw()).thenReturn(completeMessage);
setupMessageConfig();
Member author = Mockito.mock(Member.class);
when(message.getMember()).thenReturn(author);
when(message.getGuild()).thenReturn(guild);
when(context.getMember()).thenReturn(author);
when(context.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(FIRST_SERVER_ID);
when(userInServerManagementService.loadOrCreateUser(author)).thenReturn(embeddingUser);
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
@@ -185,7 +188,7 @@ public class MessageEmbedListenerTest {
when(message.getContentRaw()).thenReturn(text);
setupMessageConfig();
Member author = Mockito.mock(Member.class);
when(message.getMember()).thenReturn(author);
when(context.getMember()).thenReturn(author);
when(userInServerManagementService.loadOrCreateUser(author)).thenReturn(userInAServer);
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
CachedMessage secondCachedMessage = Mockito.mock(CachedMessage.class);
@@ -203,8 +206,8 @@ public class MessageEmbedListenerTest {
public void testLoadUserAndEmbed() {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
long userId = 3L;
when(message.getTextChannel()).thenReturn(textChannel);
when(messageEmbedService.embedLink(cachedMessage, textChannel, userId, message)).thenReturn(CompletableFuture.completedFuture(null));
when(context.getGuildChannel()).thenReturn(textChannel);
when(messageEmbedService.embedLink(cachedMessage, textChannel, userId, context)).thenReturn(CompletableFuture.completedFuture(null));
testUnit.embedSingleLink(message, userId, cachedMessage);
verify(metricService, times(1)).incrementCounter(any());
}
@@ -220,7 +223,7 @@ public class MessageEmbedListenerTest {
when(foundLink.getChannelId()).thenReturn(FIRST_CHANNEL_ID);
List<MessageEmbedLink> foundMessageLinks = Arrays.asList(foundLink);
Member author = Mockito.mock(Member.class);
when(message.getMember()).thenReturn(author);
when(context.getMember()).thenReturn(author);
when(userInServerManagementService.loadOrCreateUser(author)).thenReturn(userInAServer);
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage));

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.linkembed.service;
import dev.sheldan.abstracto.core.models.GuildMemberMessageChannel;
import dev.sheldan.abstracto.core.models.cache.CachedAuthor;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUser;
@@ -73,10 +74,13 @@ public class MessageEmbedServiceBeanTest {
private FeatureModeService featureModeService;
@Mock
private TextChannel textChannel;
private GuildMessageChannel textChannel;
@Mock
private Message embeddingMessage;
private GuildMemberMessageChannel embeddingMessage;
@Mock
private Message message;
@Mock
private Guild guild;
@@ -176,7 +180,7 @@ public class MessageEmbedServiceBeanTest {
when(messageCache.getMessageFromCache(SERVER_ID,channelId, firstMessageId)).thenReturn(CompletableFuture.completedFuture(firstCachedMessage));
Long embeddingUserId = 5L;
testUnit.embedLinks(linksToEmbed, textChannel, embeddingUserId, embeddingMessage);
verify( self, times(1)).embedLink(eq(firstCachedMessage), eq(textChannel), eq(embeddingUserId) , eq(embeddingMessage));
verify( self, times(1)).embedLink(firstCachedMessage, textChannel, embeddingUserId, embeddingMessage);
}
@Test
@@ -258,17 +262,17 @@ public class MessageEmbedServiceBeanTest {
@Test
public void testLoadUserAndPersistMessage() {
when(userInServerManagementService.loadOrCreateUser(EMBEDDING_USER_IN_SERVER_ID)).thenReturn(embeddingUser);
testUnit.loadUserAndPersistMessage(cachedMessage, EMBEDDING_USER_IN_SERVER_ID, embeddingMessage, null);
verify(messageEmbedPostManagementService, times(1)).createMessageEmbed(cachedMessage, embeddingMessage, embeddingUser, null);
testUnit.loadUserAndPersistMessage(cachedMessage, EMBEDDING_USER_IN_SERVER_ID, message, null);
verify(messageEmbedPostManagementService, times(1)).createMessageEmbed(cachedMessage, message, embeddingUser, null);
}
@Test
public void testLoadMessageEmbedModel() {
when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getChannelId()).thenReturn(CHANNEL_ID);
when(channelService.getTextChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.of(textChannel));
when(channelService.getMessageChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.of(textChannel));
when(embeddingMessage.getGuild()).thenReturn(guild);
when(embeddingMessage.getChannel()).thenReturn(textChannel);
when(embeddingMessage.getGuildChannel()).thenReturn(textChannel);
when(embeddingMessage.getMember()).thenReturn(embeddingMember);
MessageEmbeddedModel createdModel = testUnit.loadMessageEmbedModel(embeddingMessage, cachedMessage, embeddedUser, false);
Assert.assertEquals(textChannel, createdModel.getSourceChannel());

View File

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

View File

@@ -6,8 +6,8 @@ import dev.sheldan.abstracto.core.models.template.button.ButtonConfigModel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.User;
@Getter
@@ -16,7 +16,7 @@ import net.dv8tion.jda.api.entities.User;
public class MessageEmbeddedModel extends UserInitiatedServerContext {
private CachedMessage embeddedMessage;
private User author;
private TextChannel sourceChannel;
private GuildMessageChannel sourceChannel;
private Member embeddingUser;
private ButtonConfigModel buttonConfigModel;
private Long referencedMessageId;

View File

@@ -1,16 +1,18 @@
package dev.sheldan.abstracto.linkembed.service;
import dev.sheldan.abstracto.core.models.GuildMemberMessageChannel;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.linkembed.model.MessageEmbedLink;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.interactions.commands.CommandInteraction;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public interface MessageEmbedService {
List<MessageEmbedLink> getLinksInMessage(String message);
void embedLinks(List<MessageEmbedLink> linksToEmbed, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage);
CompletableFuture<Void> embedLink(CachedMessage cachedMessage, TextChannel target, Long userEmbeddingUserInServerId, Message embeddingMessage);
void embedLinks(List<MessageEmbedLink> linksToEmbed, GuildMessageChannel target, Long userEmbeddingUserInServerId, GuildMemberMessageChannel executionContext);
CompletableFuture<Void> embedLink(CachedMessage cachedMessage, GuildMessageChannel target, Long userEmbeddingUserInServerId, GuildMemberMessageChannel executionContext);
CompletableFuture<Void> embedLink(CachedMessage cachedMessage, GuildMessageChannel target, Long userEmbeddingUserInServerId, GuildMemberMessageChannel executionContext, CommandInteraction interaction);
CompletableFuture<Void> cleanUpOldMessageEmbeds();
}

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -20,6 +20,7 @@ import dev.sheldan.abstracto.logging.config.LoggingPostTarget;
import dev.sheldan.abstracto.logging.model.template.MessageDeletedAttachmentLog;
import dev.sheldan.abstracto.logging.model.template.MessageDeletedLog;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
@@ -76,7 +77,7 @@ public class MessageDeleteLogListener implements AsyncMessageDeletedListener {
public void executeListener(CachedMessage messageFromCache, Member authorMember) {
log.debug("Message {} in channel {} in guild {} was deleted.", messageFromCache.getMessageId(), messageFromCache.getChannelId(), messageFromCache.getServerId());
TextChannel textChannel = channelService.getTextChannelFromServer(messageFromCache.getServerId(), messageFromCache.getChannelId());
GuildMessageChannel textChannel = channelService.getMessageChannelFromServer(messageFromCache.getServerId(), messageFromCache.getChannelId());
MessageDeletedLog logModel = MessageDeletedLog
.builder()
.cachedMessage(messageFromCache)

View File

@@ -17,6 +17,7 @@ import dev.sheldan.abstracto.logging.config.LoggingPostTarget;
import dev.sheldan.abstracto.logging.model.template.MessageDeletedAttachmentLog;
import dev.sheldan.abstracto.logging.model.template.MessageEditedLog;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
@@ -56,7 +57,7 @@ public class MessageEditedListener implements AsyncMessageUpdatedListener {
return DefaultListenerResult.IGNORED;
}
log.debug("Message {} in channel {} in guild {} was edited.", messageBefore.getMessageId(), messageBefore.getChannelId(), model.getServerId());
TextChannel textChannel = channelService.getTextChannelFromServer(model.getServerId(), messageBefore.getChannelId());
GuildMessageChannel textChannel = channelService.getMessageChannelFromServer(model.getServerId(), messageBefore.getChannelId());
MessageEditedLog lodModel = MessageEditedLog
.builder()
.messageAfter(messageAfter)

View File

@@ -108,7 +108,7 @@ public class MessageDeleteLogListenerTest {
MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
when(member.getGuild()).thenReturn(guild);
when(templateService.renderEmbedTemplate(eq(MessageDeleteLogListener.MESSAGE_DELETED_TEMPLATE), captor.capture(), eq(SERVER_ID))).thenReturn(messageToSend);
when(channelService.getTextChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(textChannel);
when(channelService.getMessageChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(textChannel);
testUnit.executeListener(deletedMessage, member);
verify(postTargetService, times(1)).sendEmbedInPostTarget(messageToSend, LoggingPostTarget.DELETE_LOG, SERVER_ID);
MessageDeletedLog messageDeletedLog = captor.getValue();
@@ -123,7 +123,7 @@ public class MessageDeleteLogListenerTest {
String attachmentUrl = "url";
when(deletedMessage.getServerId()).thenReturn(SERVER_ID);
when(deletedMessage.getChannelId()).thenReturn(CHANNEL_ID);
when(channelService.getTextChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(textChannel);
when(channelService.getMessageChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(textChannel);
CachedAttachment cachedAttachment = Mockito.mock(CachedAttachment.class);
when(cachedAttachment.getProxyUrl()).thenReturn(attachmentUrl);
List<CachedAttachment> attachmentList = Arrays.asList(cachedAttachment);
@@ -147,7 +147,7 @@ public class MessageDeleteLogListenerTest {
public void testExecuteListenerWithTwoAttachment() {
when(deletedMessage.getServerId()).thenReturn(SERVER_ID);
when(deletedMessage.getChannelId()).thenReturn(CHANNEL_ID);
when(channelService.getTextChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(textChannel);
when(channelService.getMessageChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(textChannel);
String attachmentUrl = "url";
String secondAttachmentUrl = "url2";
CachedAttachment cachedAttachment = Mockito.mock(CachedAttachment.class);

View File

@@ -79,7 +79,7 @@ public class MessageEditedListenerTest {
MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
ArgumentCaptor<MessageEditedLog> captor = ArgumentCaptor.forClass(MessageEditedLog.class);
when(templateService.renderEmbedTemplate(eq(MessageEditedListener.MESSAGE_EDITED_TEMPLATE), captor.capture(), eq(SERVER_ID))).thenReturn(messageToSend);
when(channelService.getTextChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(channel);
when(channelService.getMessageChannelFromServer(SERVER_ID, CHANNEL_ID)).thenReturn(channel);
when(model.getAfter()).thenReturn(messageAfter);
when(model.getBefore()).thenReturn(messageBefore);
when(messageAfter.getMember()).thenReturn(author);

View File

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

View File

@@ -1,7 +1,9 @@
package dev.sheldan.abstracto.logging.model.template;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
@@ -12,6 +14,8 @@ import lombok.experimental.SuperBuilder;
@Getter
@Setter
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class MessageDeletedAttachmentLog extends SlimUserInitiatedServerContext {
/**
* The proxy URL to the attachment which was deleted.

View File

@@ -2,7 +2,9 @@ package dev.sheldan.abstracto.logging.model.template;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
@@ -12,6 +14,8 @@ import lombok.experimental.SuperBuilder;
@Getter
@Setter
@SuperBuilder
@AllArgsConstructor
@NoArgsConstructor
public class MessageDeletedLog extends SlimUserInitiatedServerContext {
/**
* A {@link CachedMessage} representing the deleted message

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -43,7 +43,11 @@ public class SlowMode extends AbstractConditionableCommand {
throw new EntityGuildMismatchException();
}
} else {
channel = commandContext.getChannel();
if(commandContext.getChannel() instanceof TextChannel) {
channel = (TextChannel) commandContext.getChannel();
} else {
throw new IllegalArgumentException("Not a text channel.");
}
}
return slowModeService.setSlowMode(channel, duration)
.thenApply(aVoid -> CommandResult.fromSuccess());

View File

@@ -28,10 +28,7 @@ import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@@ -159,7 +156,7 @@ public class MuteServiceBean implements MuteService {
String muteNotificationMessage = templateService.renderTemplate(MUTE_NOTIFICATION_TEMPLATE, muteNotification, message.getServerId());
CompletableFuture<Message> messageCompletableFuture = messageService.sendMessageToUser(memberBeingMuted.getUser(), muteNotificationMessage);
messageCompletableFuture.exceptionally(throwable -> {
TextChannel feedBackChannel = channelService.getTextChannelFromServer(message.getServerId(), message.getChannelId());
GuildMessageChannel feedBackChannel = channelService.getMessageChannelFromServer(message.getServerId(), message.getChannelId());
channelService.sendTextToChannel(throwable.getMessage(), feedBackChannel).whenComplete((exceptionMessage, innerThrowable) -> {
notificationFuture.complete(null);
log.info("Successfully notified user {} in server {} about mute.", memberBeingMuted.getId(), memberBeingMuted.getGuild().getId());

View File

@@ -9,10 +9,7 @@ import dev.sheldan.abstracto.moderation.model.template.command.PurgeStatusUpdate
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageHistory;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.utils.MiscUtil;
import net.dv8tion.jda.api.utils.TimeUtil;
import org.springframework.beans.factory.annotation.Autowired;
@@ -51,11 +48,11 @@ public class PurgeServiceBean implements PurgeService {
.build();
@Override
public CompletableFuture<Void> purgeMessagesInChannel(Integer amountToDelete, TextChannel channel, Long startId, Member purgedMember) {
public CompletableFuture<Void> purgeMessagesInChannel(Integer amountToDelete, GuildMessageChannel channel, Long startId, Member purgedMember) {
return purgeMessages(amountToDelete, channel, startId, purgedMember, amountToDelete, 0, 0L);
}
private CompletableFuture<Void> purgeMessages(Integer amountToDelete, TextChannel channel, Long startId, Member purgedMember, Integer totalCount, Integer currentCount, Long statusMessageId) {
private CompletableFuture<Void> purgeMessages(Integer amountToDelete, GuildMessageChannel channel, Long startId, Member purgedMember, Integer totalCount, Integer currentCount, Long statusMessageId) {
int toDeleteInThisIteration;
if(amountToDelete >= PURGE_MAX_MESSAGES){
@@ -106,9 +103,12 @@ public class PurgeServiceBean implements PurgeService {
return CompletableFuture.allOf(retrievalFuture, deletionFuture);
}
private void bulkDeleteMessages(TextChannel channel, CompletableFuture<Void> deletionFuture, List<Message> messagesToDeleteNow, Consumer<Void> consumer) {
private void bulkDeleteMessages(GuildMessageChannel channel, CompletableFuture<Void> deletionFuture, List<Message> messagesToDeleteNow, Consumer<Void> consumer) {
try {
channelService.deleteMessagesInChannel(channel, messagesToDeleteNow).queue(consumer, deletionFuture::completeExceptionally);
channelService.deleteMessagesInChannel(channel, messagesToDeleteNow).thenAccept(consumer).exceptionally(throwable -> {
deletionFuture.completeExceptionally(throwable);
return null;
});
} catch (IllegalArgumentException e) {
channelService.sendTextToChannel(e.getMessage(), channel);
log.warn("Failed to bulk delete, message was most likely too old to delete by bulk.", e);
@@ -116,7 +116,7 @@ public class PurgeServiceBean implements PurgeService {
}
}
private CompletableFuture<Long> getOrCreatedStatusMessage(TextChannel channel, Integer totalCount, Long statusMessageId) {
private CompletableFuture<Long> getOrCreatedStatusMessage(GuildMessageChannel channel, Integer totalCount, Long statusMessageId) {
CompletableFuture<Long> statusMessageFuture;
if(statusMessageId == 0) {
log.debug("Creating new status message in channel {} in server {} because of puring.", channel.getIdLong(), channel.getGuild().getId());
@@ -151,7 +151,7 @@ public class PurgeServiceBean implements PurgeService {
return messagesToDeleteNow;
}
private Consumer<Void> deletionConsumer(Integer amountToDelete, TextChannel channel, Member purgedMember, Integer totalCount, Integer currentCount, CompletableFuture<Void> deletionFuture, Long currentStatusMessageId, Message earliestMessage) {
private Consumer<Void> deletionConsumer(Integer amountToDelete, GuildMessageChannel channel, Member purgedMember, Integer totalCount, Integer currentCount, CompletableFuture<Void> deletionFuture, Long currentStatusMessageId, Message earliestMessage) {
return aVoid -> {
if (amountToDelete >= 1) {
log.debug("Still more than 1 message to delete. Continuing.");
@@ -181,7 +181,7 @@ public class PurgeServiceBean implements PurgeService {
}
@Override
public CompletableFuture<Void> purgeMessagesInChannel(Integer count, TextChannel channel, Message origin, Member purgingRestriction) {
public CompletableFuture<Void> purgeMessagesInChannel(Integer count, GuildMessageChannel channel, Message origin, Member purgingRestriction) {
return purgeMessagesInChannel(count, channel, origin.getIdLong(), purgingRestriction);
}

View File

@@ -17,6 +17,7 @@ import dev.sheldan.abstracto.moderation.model.template.listener.ReportReactionNo
import dev.sheldan.abstracto.moderation.service.management.ModerationUserManagementService;
import dev.sheldan.abstracto.moderation.service.management.ReactionReportManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
@@ -92,7 +93,7 @@ public class ReactionReportServiceBean implements ReactionReportService {
ReactionReport report = recentReportOptional.get();
log.info("Report is already present in channel {} with message {}. Updating field.", report.getReportChannel().getId(), report.getReportMessageId());
report.setReportCount(report.getReportCount() + 1);
TextChannel reportTextChannel = channelService.getTextChannelFromServer(serverId, report.getReportChannel().getId());
GuildMessageChannel reportTextChannel = channelService.getMessageChannelFromServer(serverId, report.getReportChannel().getId());
return channelService.editFieldValueInMessage(reportTextChannel, report.getReportMessageId(), 0, report.getReportCount().toString())
.thenAccept(message -> self.updateModerationUserReportCooldown(reporter));
}

View File

@@ -4,6 +4,7 @@ import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.service.ChannelService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

@@ -35,7 +35,7 @@ public class ReactionReportManagementServiceBean implements ReactionReportManage
@Override
public ReactionReport createReactionReport(CachedMessage reportedMessage, Message reportMessage) {
AChannel reportChannel = channelManagementService.loadChannel(reportMessage.getTextChannel());
AChannel reportChannel = channelManagementService.loadChannel(reportMessage.getChannel());
AChannel reportedChannel = channelManagementService.loadChannel(reportedMessage.getChannelId());
AUserInAServer reportedUser = userInServerManagementService.loadOrCreateUser(reportedMessage.getAuthorAsServerUser());
ReactionReport report = ReactionReport

View File

@@ -32,7 +32,7 @@ public class SlowModeTest {
public void testExecuteSlowModeWithDurationCurrentChannel() {
String duration = "1m";
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(duration));
when(slowModeService.setSlowMode(parameters.getChannel(), Duration.ofMinutes(1))).thenReturn(CompletableFuture.completedFuture(null));
when(slowModeService.setSlowMode((TextChannel) parameters.getChannel(), Duration.ofMinutes(1))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
}
@@ -41,7 +41,7 @@ public class SlowModeTest {
public void testDisableSlowModeCurrentChannel() {
String duration = "off";
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(duration));
when(slowModeService.setSlowMode(parameters.getChannel(), Duration.ZERO)).thenReturn(CompletableFuture.completedFuture(null));
when(slowModeService.setSlowMode((TextChannel) parameters.getChannel(), Duration.ZERO)).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
}

View File

@@ -1,300 +0,0 @@
package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.moderation.exception.NoMessageFoundException;
import dev.sheldan.abstracto.moderation.model.template.command.PurgeStatusUpdateModel;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
import net.dv8tion.jda.api.utils.TimeUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class PurgeServiceBeanTest {
@InjectMocks
private PurgeServiceBean testUnit;
@Mock
private ChannelService channelService;
@Mock
private MessageService messageService;
@Mock
private TemplateService templateService;
@Mock
private MetricService metricService;
@Mock
private TextChannel textChannel;
@Mock
private Member purgedMember;
private static final Long START_MESSAGE_ID = 4L;
private static final Long STATUS_MESSAGE_ID = 7L;
private static final Long AUTHOR_ID = 17L;
@Mock
private User messageAuthor;
@Mock
private Message firstMessage;
@Mock
private Message secondMessage;
@Mock
private Message thirdMessage;
@Mock
private Message fourthMessage;
@Mock
private MessageHistory history;
@Mock
private MessageToSend firstStatusUpdateMessage;
@Mock
private RestAction deleteMessagesAction;
@Mock
private AuditableRestAction deleteStatusAction;
@Mock
private Guild guild;
private static final Long SERVER_ID = 1L;
@Before
public void setup() {
when(textChannel.getGuild()).thenReturn(guild);
when(guild.getId()).thenReturn(SERVER_ID.toString());
when(guild.getIdLong()).thenReturn(SERVER_ID);
}
@Test
public void testPurgeMessageViaStartMessage() {
Integer amountToDelete = 50;
when(channelService.getHistoryOfChannel(textChannel, START_MESSAGE_ID, amountToDelete)).thenReturn(CompletableFuture.completedFuture(history));
setupOneMessageBatch(getDeletableMessageId(), getDeletableMessageId());
Message messageToStartOffAT = Mockito.mock(Message.class);
when(messageToStartOffAT.getIdLong()).thenReturn(START_MESSAGE_ID);
CompletableFuture<Void> futures = testUnit.purgeMessagesInChannel(amountToDelete, textChannel, messageToStartOffAT, purgedMember);
futures.whenComplete((aVoid, throwable) -> Assert.assertNull(throwable));
verify(deleteStatusAction, times(1)).queueAfter(5, TimeUnit.SECONDS);
verify(messageService, times(1)).updateStatusMessage(eq(textChannel), anyLong(), any());
verify(metricService, times(1)).incrementCounter(any(), eq((long) amountToDelete));
}
@Test
public void testPurgeMessageNotNoUser() {
Integer amountToDelete = 50;
when(channelService.getHistoryOfChannel(textChannel, START_MESSAGE_ID, amountToDelete)).thenReturn(CompletableFuture.completedFuture(history));
when(firstMessage.getId()).thenReturn(getDeletableMessageId().toString());
when(secondMessage.getId()).thenReturn(getDeletableMessageId().toString());
setupFirstMessageHistoryMocks();
setupStatusMessageMocks();
mockQueueDoubleVoidConsumer(deleteMessagesAction);
CompletableFuture<Void> futures = testUnit.purgeMessagesInChannel(amountToDelete, textChannel, START_MESSAGE_ID, null);
futures.whenComplete((aVoid, throwable) -> Assert.assertNull(throwable));
verify(deleteStatusAction, times(1)).queueAfter(5, TimeUnit.SECONDS);
verify(messageService, times(1)).updateStatusMessage(eq(textChannel), anyLong(), any());
verify(metricService, times(1)).incrementCounter(any(), eq((long) amountToDelete));
}
@Test
public void testPurgeSingleMessage() {
Integer amountToDelete = 50;
when(channelService.getHistoryOfChannel(textChannel, START_MESSAGE_ID, amountToDelete)).thenReturn(CompletableFuture.completedFuture(history));
when(firstMessage.getId()).thenReturn(getDeletableMessageId().toString());
when(firstMessage.getAuthor()).thenReturn(messageAuthor);
setupMembersWithAuthorId();
List<Message> messagesToDelete = Arrays.asList(firstMessage);
when(history.getRetrievedHistory()).thenReturn(messagesToDelete);
setupStatusMessageMocks();
AuditableRestAction auditableRestAction = Mockito.mock(AuditableRestAction.class);
when(messageService.deleteMessageWithAction(firstMessage)).thenReturn(auditableRestAction);
mockQueueDoubleVoidConsumer(auditableRestAction);
CompletableFuture<Void> futures = testUnit.purgeMessagesInChannel(amountToDelete, textChannel, START_MESSAGE_ID, purgedMember);
futures.whenComplete((aVoid, throwable) -> Assert.assertNull(throwable));
verify(deleteStatusAction, times(1)).queueAfter(5, TimeUnit.SECONDS);
verify(messageService, times(1)).updateStatusMessage(eq(textChannel), anyLong(), any());
verify(metricService, times(1)).incrementCounter(any(), eq((long) amountToDelete));
}
@Test
public void testPurgeMessagesInTwoIterationsSecondIterationsTooOld() {
Integer amountToDelete = 150;
Long latestDeletedMessageId = getDeletableMessageId();
when(channelService.getHistoryOfChannel(textChannel, START_MESSAGE_ID, amountToDelete - 50)).thenReturn(CompletableFuture.completedFuture(history));
MessageHistory secondHistory = Mockito.mock(MessageHistory.class);
when(channelService.getHistoryOfChannel(textChannel, latestDeletedMessageId, 50)).thenReturn(CompletableFuture.completedFuture(secondHistory));
when(secondMessage.getIdLong()).thenReturn(latestDeletedMessageId);
when(thirdMessage.getId()).thenReturn(getNotDeletableMessageId().toString());
when(fourthMessage.getId()).thenReturn(getNotDeletableMessageId().toString());
setupOneMessageBatch(getDeletableMessageId(), latestDeletedMessageId);
List<Message> secondMessagesToDelete = Arrays.asList(thirdMessage, fourthMessage);
when(secondHistory.getRetrievedHistory()).thenReturn(secondMessagesToDelete);
CompletableFuture<Void> futures = testUnit.purgeMessagesInChannel(amountToDelete, textChannel, START_MESSAGE_ID, purgedMember);
futures.whenComplete((aVoid, throwable) -> Assert.assertTrue(throwable.getCause() instanceof NoMessageFoundException));
verify(deleteStatusAction, times(1)).queueAfter(5, TimeUnit.SECONDS);
verify(messageService, times(1)).updateStatusMessage(eq(textChannel), anyLong(), any());
ArgumentCaptor<Long> deleted = ArgumentCaptor.forClass(Long.class);
verify(metricService, times(2)).incrementCounter(any(), deleted.capture());
List<Long> capturedValues = deleted.getAllValues();
Assert.assertEquals(2, capturedValues.size());
Assert.assertEquals(100, capturedValues.get(0).longValue());
Assert.assertEquals(50, capturedValues.get(1).longValue());
}
@Test
public void testPurgeMessagesInTwoIterations() {
Integer amountToDelete = 150;
Long latestDeletedMessageId = getDeletableMessageId();
when(channelService.getHistoryOfChannel(textChannel, START_MESSAGE_ID, amountToDelete - 50)).thenReturn(CompletableFuture.completedFuture(history));
MessageHistory secondHistory = Mockito.mock(MessageHistory.class);
when(channelService.getHistoryOfChannel(textChannel, latestDeletedMessageId, 50)).thenReturn(CompletableFuture.completedFuture(secondHistory));
when(secondMessage.getIdLong()).thenReturn(latestDeletedMessageId);
setupOneMessageBatch(getDeletableMessageId(), latestDeletedMessageId);
setupFirstMessages(thirdMessage, getDeletableMessageId(), fourthMessage, latestDeletedMessageId, messageAuthor);
RestAction secondDeleteMessagesAction = Mockito.mock(RestAction.class);
List<Message> secondMessagesToDelete = Arrays.asList(thirdMessage, fourthMessage);
when(secondHistory.getRetrievedHistory()).thenReturn(secondMessagesToDelete);
when(channelService.deleteMessagesInChannel(textChannel, secondMessagesToDelete)).thenReturn(secondDeleteMessagesAction);
mockQueueDoubleVoidConsumer(secondDeleteMessagesAction);
CompletableFuture<Void> futures = testUnit.purgeMessagesInChannel(amountToDelete, textChannel, START_MESSAGE_ID, purgedMember);
futures.whenComplete((aVoid, throwable) -> Assert.assertNull(throwable));
verify(deleteStatusAction, times(1)).queueAfter(5, TimeUnit.SECONDS);
verify(messageService, times(2)).updateStatusMessage(eq(textChannel), anyLong(), any());
ArgumentCaptor<Long> deleted = ArgumentCaptor.forClass(Long.class);
verify(metricService, times(2)).incrementCounter(any(), deleted.capture());
List<Long> capturedValues = deleted.getAllValues();
Assert.assertEquals(2, capturedValues.size());
Assert.assertEquals(100, capturedValues.get(0).longValue());
Assert.assertEquals(50, capturedValues.get(1).longValue());
}
@Test
public void testPurgeMessagesInOneIteration() {
Integer amountToDelete = 50;
when(channelService.getHistoryOfChannel(textChannel, START_MESSAGE_ID, amountToDelete)).thenReturn(CompletableFuture.completedFuture(history));
setupOneMessageBatch(getDeletableMessageId(), getDeletableMessageId());
CompletableFuture<Void> futures = testUnit.purgeMessagesInChannel(amountToDelete, textChannel, START_MESSAGE_ID, purgedMember);
futures.whenComplete((aVoid, throwable) -> Assert.assertNull(throwable));
verify(deleteStatusAction, times(1)).queueAfter(5, TimeUnit.SECONDS);
verify(messageService, times(1)).updateStatusMessage(eq(textChannel), anyLong(), any());
verify(metricService, times(1)).incrementCounter(any(), eq((long) amountToDelete));
}
@Test
public void testPurgeTooOldMessage() {
Integer amountToDelete = 50;
when(channelService.getHistoryOfChannel(textChannel, START_MESSAGE_ID, amountToDelete)).thenReturn(CompletableFuture.completedFuture(history));
when(firstMessage.getId()).thenReturn(getNotDeletableMessageId().toString());
when(history.getRetrievedHistory()).thenReturn(Arrays.asList(firstMessage));
setupStatusMessageMocks();
CompletableFuture<Void> futures = testUnit.purgeMessagesInChannel(amountToDelete, textChannel, START_MESSAGE_ID, purgedMember);
futures.whenComplete((aVoid, throwable) -> Assert.assertTrue(throwable.getCause() instanceof NoMessageFoundException));
}
private void setupOneMessageBatch(Long deletableMessageId, Long deletableMessageId2) {
setupFirstMessages(firstMessage, deletableMessageId, secondMessage, deletableMessageId2, messageAuthor);
setupMembersWithAuthorId();
setupFirstMessageHistoryMocks();
mockQueueDoubleVoidConsumer(deleteMessagesAction);
setupStatusMessageMocks();
}
public void mockQueueDoubleVoidConsumer(RestAction action) {
doAnswer(invocationOnMock -> {
Object consumerObj = invocationOnMock.getArguments()[0];
if(consumerObj instanceof Consumer) {
Consumer<Void> consumer = (Consumer) consumerObj;
consumer.accept(null);
}
return null;
}).when(action).queue(any(Consumer.class), any(Consumer.class));
}
private void setupFirstMessageHistoryMocks() {
List<Message> messagesToDelete = Arrays.asList(firstMessage, secondMessage);
when(history.getRetrievedHistory()).thenReturn(messagesToDelete);
when(channelService.deleteMessagesInChannel(textChannel, messagesToDelete)).thenReturn(deleteMessagesAction);
}
private void setupStatusMessageMocks() {
when(templateService.renderTemplateToMessageToSend(eq("purge_status_update"), any(PurgeStatusUpdateModel.class), eq(SERVER_ID))).thenReturn(firstStatusUpdateMessage);
when(messageService.createStatusMessageId(firstStatusUpdateMessage, textChannel)).thenReturn(CompletableFuture.completedFuture(STATUS_MESSAGE_ID));
when(textChannel.deleteMessageById(STATUS_MESSAGE_ID)).thenReturn(deleteStatusAction);
}
private void setupMembersWithAuthorId() {
when(messageAuthor.getIdLong()).thenReturn(AUTHOR_ID);
when(purgedMember.getIdLong()).thenReturn(AUTHOR_ID);
}
private void setupFirstMessages(Message firstMessageToMock, Long firstMessageId, Message secondMessageToMock, Long secondMessageId, User author) {
when(firstMessageToMock.getId()).thenReturn(firstMessageId.toString());
when(firstMessageToMock.getAuthor()).thenReturn(author);
when(secondMessageToMock.getId()).thenReturn(secondMessageId.toString());
when(secondMessageToMock.getAuthor()).thenReturn(author);
}
private Long getDeletableMessageId() {
return TimeUtil.getDiscordTimestamp((System.currentTimeMillis() - (7 * 24 * 60 * 60 * 1000)));
}
private Long getNotDeletableMessageId() {
return TimeUtil.getDiscordTimestamp((System.currentTimeMillis() - (21 * 24 * 60 * 60 * 1000)));
}
}

View File

@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.managers.ChannelManager;
import net.dv8tion.jda.api.managers.channel.ChannelManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;

View File

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

View File

@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.context.ServerContext;
import dev.sheldan.abstracto.core.utils.MessageUtils;
import dev.sheldan.abstracto.moderation.model.database.Mute;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@@ -19,6 +20,7 @@ import java.time.Instant;
@SuperBuilder
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class UnMuteLog extends ServerContext {
/**
* The un-muted Member, is null if the member left the server

View File

@@ -1,12 +1,10 @@
package dev.sheldan.abstracto.moderation.service;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.*;
import java.util.concurrent.CompletableFuture;
public interface PurgeService {
CompletableFuture<Void> purgeMessagesInChannel(Integer count, TextChannel channel, Long messageId, Member purgingRestriction);
CompletableFuture<Void> purgeMessagesInChannel(Integer count, TextChannel channel, Message origin, Member purgingRestriction);
CompletableFuture<Void> purgeMessagesInChannel(Integer count, GuildMessageChannel channel, Long messageId, Member purgingRestriction);
CompletableFuture<Void> purgeMessagesInChannel(Integer count, GuildMessageChannel channel, Message origin, Member purgingRestriction);
}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.3.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -0,0 +1,98 @@
package dev.sheldan.abstracto.modmail.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.ButtonClickedListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.ButtonClickedListener;
import dev.sheldan.abstracto.core.models.UndoActionInstance;
import dev.sheldan.abstracto.core.models.listener.ButtonClickedListenerModel;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.ComponentPayloadManagementService;
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
import dev.sheldan.abstracto.modmail.model.dto.ServerChoicePayload;
import dev.sheldan.abstracto.modmail.model.dto.ServiceChoicesPayload;
import dev.sheldan.abstracto.modmail.service.ModMailThreadService;
import dev.sheldan.abstracto.modmail.service.ModMailThreadServiceBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
@Component
@Slf4j
public class ModMailInitialButtonListener implements ButtonClickedListener {
@Autowired
private MemberService memberService;
@Autowired
private ModMailThreadService modMailThreadService;
@Autowired
private UndoActionService undoActionService;
@Autowired
private MessageService messageService;
@Autowired
private ComponentPayloadManagementService componentPayloadService;
@Autowired
private ModMailInitialButtonListener self;
@Autowired
private ChannelService channelService;
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
ServiceChoicesPayload choices = (ServiceChoicesPayload) model.getDeserializedPayload();
ServerChoicePayload chosenServer = choices.getChoices().get(model.getEvent().getComponentId());
Long userId = choices.getUserId();
log.debug("Executing action for creationg a modmail thread in server {} for user {}.", chosenServer.getServerId(), userId);
ArrayList<UndoActionInstance> undoActions = new ArrayList<>();
memberService.getMemberInServerAsync(chosenServer.getServerId(), userId)
.thenCompose(member -> channelService.retrieveMessageInChannel(model.getEvent().getChannel(), choices.getMessageId())
.thenCompose(originalMessage -> {
try {
return modMailThreadService.createModMailThreadForUser(member, originalMessage, model.getEvent().getChannel(), true, undoActions);
} catch (Exception ex) {
log.error("Failed to setup thread correctly", ex);
undoActionService.performActions(undoActions);
return null;
}
})
.thenAccept(unused -> self.cleanup(model)))
.exceptionally(throwable -> {
log.error("Failed to setup thread correctly", throwable);
undoActionService.performActions(undoActions);
return null;
});
return ButtonClickedListenerResult.ACKNOWLEDGED;
}
@Transactional
public void cleanup(ButtonClickedListenerModel model) {
ServiceChoicesPayload choices = (ServiceChoicesPayload) model.getDeserializedPayload();
choices.getChoices().keySet().forEach(componentId -> componentPayloadService.deletePayload(componentId));
}
@Override
public Boolean handlesEvent(ButtonClickedListenerModel model) {
return ModMailThreadServiceBean.MODMAIL_INITIAL_ORIGIN.equals(model.getOrigin()) && !model.getEvent().isFromGuild();
}
@Override
public Integer getPriority() {
return ListenerPriority.LOW;
}
@Override
public FeatureDefinition getFeature() {
return ModMailFeatureDefinition.MOD_MAIL;
}
}

View File

@@ -72,9 +72,9 @@ public class ModMailMessageServiceBean implements ModMailMessageService {
.stream()
.map(ServerChannelMessageUser::getMessageId)
.collect(Collectors.toList());
Optional<TextChannel> textChannelFromServer = channelService.getTextChannelFromServerOptional(thread.getServer().getId(), thread.getChannel().getId());
Optional<GuildMessageChannel> textChannelFromServer = channelService.getMessageChannelFromServerOptional(thread.getServer().getId(), thread.getChannel().getId());
if(textChannelFromServer.isPresent()) {
TextChannel modMailThread = textChannelFromServer.get();
GuildMessageChannel modMailThread = textChannelFromServer.get();
Long userId = thread.getUser().getUserReference().getId();
botService.getInstance().openPrivateChannelById(userId).queue(privateChannel -> {
Optional<ServerChannelMessageUser> latestThreadMessageOptional = messageIds
@@ -125,7 +125,7 @@ public class ModMailMessageServiceBean implements ModMailMessageService {
}
public CompletableFuture<Void> loadMoreMessages(Integer totalMessageCount, List<Long> messagesToLoad,
MessageHistory privateMessageHistory, TextChannel thread,
MessageHistory privateMessageHistory, GuildMessageChannel thread,
MessageHistory threadMessageHistory, PrivateChannel dmChannel, List<Message> loadedMessages, Integer counter) {
// TODO maybe find a better mechanism for this... one which does not lead to infinite loops, but also doesnt miss out on history
if(counter.equals(totalMessageCount * 2)) {

View File

@@ -1,7 +1,5 @@
package dev.sheldan.abstracto.modmail.service;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import com.jagrosh.jdautilities.menu.ButtonMenu;
import dev.sheldan.abstracto.core.command.exception.AbstractoTemplatedException;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
@@ -26,8 +24,10 @@ import dev.sheldan.abstracto.modmail.exception.ModMailCategoryIdException;
import dev.sheldan.abstracto.modmail.exception.ModMailThreadChannelNotFound;
import dev.sheldan.abstracto.modmail.exception.ModMailThreadNotFoundException;
import dev.sheldan.abstracto.modmail.model.ClosingContext;
import dev.sheldan.abstracto.modmail.model.dto.ServiceChoicesPayload;
import dev.sheldan.abstracto.modmail.model.template.ServerChoices;
import dev.sheldan.abstracto.modmail.model.database.*;
import dev.sheldan.abstracto.modmail.model.dto.ServerChoice;
import dev.sheldan.abstracto.modmail.model.template.ServerChoice;
import dev.sheldan.abstracto.modmail.model.template.*;
import dev.sheldan.abstracto.modmail.service.management.ModMailMessageManagementService;
import dev.sheldan.abstracto.modmail.service.management.ModMailRoleManagementService;
@@ -125,9 +125,6 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Autowired
private ModMailSubscriberManagementService modMailSubscriberManagementService;
@Autowired
private EventWaiter eventWaiter;
@Autowired
private FeatureModeService featureModeService;
@@ -146,6 +143,12 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Autowired
private MetricService metricService;
@Autowired
private ComponentService componentService;
@Autowired
private ComponentPayloadService componentPayloadService;
public static final String MODMAIL_THREAD_METRIC = "modmail.threads";
public static final String MODMAIL_MESSAGE_METRIC = "modmail.messges";
public static final String ACTION = "action";
@@ -174,14 +177,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.tagList(Arrays.asList(MetricTag.getTag(MESSAGE_DIRECTION, "sent")))
.build();
/**
* The emoji used when the user can decide for a server to open a mod mail thread in.
*/
private static List<String> NUMBER_EMOJI = Arrays.asList("\u0031\u20e3", "\u0032\u20e3", "\u0033\u20e3",
"\u0034\u20e3", "\u0035\u20e3", "\u0036\u20e3",
"\u0037\u20e3", "\u0038\u20e3", "\u0039\u20e3",
"\u0040\u20e3");
public static final String MODMAIL_INITIAL_ORIGIN = "modmailInitial";
@Override
public CompletableFuture<Void> createModMailThreadForUser(Member member, Message initialMessage, MessageChannel feedBackChannel, boolean userInitiated, List<UndoActionInstance> undoActions) {
@@ -315,34 +311,29 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Override
public void createModMailPrompt(AUser user, Message initialMessage) {
List<AUserInAServer> knownServers = userInServerManagementService.getUserInAllServers(user.getId());
// if the user doesnt exist in the servery set, we need to create the user first in all of them, in order to offer it
if(knownServers.isEmpty()) {
List<Guild> mutualServers = initialMessage.getJDA().getMutualGuilds(initialMessage.getAuthor());
mutualServers.forEach(guild -> {
AServer server = serverManagementService.loadServer(guild);
knownServers.add(userInServerManagementService.loadOrCreateUser(server, user));
});
}
if(!knownServers.isEmpty()) {
log.info("There are {} shared servers between user and the bot.", knownServers.size());
List<AServer> servers = new ArrayList<>();
List<Guild> mutualServers = initialMessage.getJDA().getMutualGuilds(initialMessage.getAuthor());
mutualServers.forEach(guild -> {
AServer server = serverManagementService.loadServer(guild);
servers.add(server);
});
if(!servers.isEmpty()) {
log.info("There are {} shared servers between user and the bot.", servers.size());
List<ServerChoice> availableGuilds = new ArrayList<>();
HashMap<String, Long> choices = new HashMap<>();
for (int i = 0; i < knownServers.size(); i++) {
AUserInAServer aUserInAServer = knownServers.get(i);
for (AServer server : servers) {
// only take the servers in which mod mail is actually enabled, would not make much sense to make the
// other servers available
if(featureFlagService.isFeatureEnabled(modMailFeatureConfig, aUserInAServer.getServerReference())) {
AServer serverReference = aUserInAServer.getServerReference();
if (featureFlagService.isFeatureEnabled(modMailFeatureConfig, server)) {
FullGuild guild = FullGuild
.builder()
.guild(guildService.getGuildById(serverReference.getId()))
.server(serverReference)
.guild(guildService.getGuildById(server.getId()))
.server(server)
.build();
ServerChoice serverChoice = ServerChoice
.builder()
.serverId(guild.getGuild().getIdLong())
.serverName(guild.getGuild().getName())
.build();
// TODO support more than this limited amount of servers
String reactionEmote = NUMBER_EMOJI.get(i);
ServerChoice serverChoice = ServerChoice.builder().guild(guild).reactionEmote(reactionEmote).build();
choices.put(reactionEmote, aUserInAServer.getServerReference().getId());
availableGuilds.add(serverChoice);
}
}
@@ -350,40 +341,30 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
// if more than 1 server is available, show a choice dialog
ArrayList<UndoActionInstance> undoActions = new ArrayList<>();
if(availableGuilds.size() > 1) {
Map<String, ServerChoice> choices = new HashMap<>();
ServerChoices serverChoices = ServerChoices
.builder()
.commonGuilds(choices)
.userId(initialMessage.getAuthor().getIdLong())
.messageId(initialMessage.getIdLong())
.build();
availableGuilds.forEach(serverChoice -> choices.put(componentService.generateComponentId(), serverChoice));
ModMailServerChooserModel modMailServerChooserModel = ModMailServerChooserModel
.builder()
.commonGuilds(availableGuilds)
.build();
String text = templateService.renderTemplate("modmail_modal_server_choice", modMailServerChooserModel);
ButtonMenu menu = new ButtonMenu.Builder()
.setChoices(choices.keySet().toArray(new String[0]))
.setEventWaiter(eventWaiter)
.setDescription(text)
.setAction(reactionEmote -> {
Long chosenServerId = choices.get(reactionEmote.getEmoji());
Long userId = initialMessage.getAuthor().getIdLong();
log.debug("Executing action for creationg a modmail thread in server {} for user {}.", chosenServerId, userId);
memberService.getMemberInServerAsync(chosenServerId, userId).thenCompose(member -> {
try {
return self.createModMailThreadForUser(member, initialMessage, initialMessage.getChannel(), true, undoActions);
} catch (Exception exception) {
log.error("Setting up modmail thread for user {} in server {} failed.", userId, chosenServerId, exception);
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(exception);
return future;
}
}).exceptionally(throwable -> {
log.error("Failed to load member {} for modmail in server {}.", userId, chosenServerId, throwable);
undoActionService.performActions(undoActions);
return null;
});
})
.choices(serverChoices)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_modal_server_choice", modMailServerChooserModel);
FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, initialMessage.getChannel()))
.thenAccept(unused -> self.persistInitialCallbacks(serverChoices))
.exceptionally(throwable -> {
log.error("Failed to setup prompt message correctly", throwable);
undoActionService.performActions(undoActions);
return null;
});
log.debug("Displaying server choice message for user {} in channel {}.", user.getId(), initialMessage.getChannel().getId());
menu.display(initialMessage.getChannel());
} else if(availableGuilds.size() == 1) {
// if exactly one server is available, open the thread directly
Long chosenServerId = choices.get(availableGuilds.get(0).getReactionEmote());
Long chosenServerId = availableGuilds.get(0).getServerId();
log.info("Only one server available to modmail. Directly opening modmail thread for user {} in server {}.", initialMessage.getAuthor().getId(), chosenServerId);
memberService.getMemberInServerAsync(chosenServerId, initialMessage.getAuthor().getIdLong()).thenCompose(member -> {
try {
@@ -408,6 +389,13 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
}
}
@Transactional
public void persistInitialCallbacks(ServerChoices choices) {
ServiceChoicesPayload payload = ServiceChoicesPayload.fromServerChoices(choices);
choices.getCommonGuilds().keySet().forEach(componentId ->
componentPayloadService.createButtonPayload(componentId, payload, MODMAIL_INITIAL_ORIGIN, null));
}
/**
* Method used to send the header of a newly created mod mail thread. This message contains information about
@@ -446,10 +434,10 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Transactional
public CompletableFuture<Message> relayMessage(Message messageFromUser, Long serverId, Long channelId, Long modmailThreadId, Member member) {
Optional<TextChannel> textChannelFromServer = channelService.getTextChannelFromServerOptional(serverId, channelId);
Optional<GuildMessageChannel> textChannelFromServer = channelService.getMessageChannelFromServerOptional(serverId, channelId);
if(textChannelFromServer.isPresent()) {
TextChannel textChannel = textChannelFromServer.get();
return self.sendUserReply(textChannel, modmailThreadId, messageFromUser, member, true);
GuildMessageChannel guildMessageChannel = textChannelFromServer.get();
return self.sendUserReply(guildMessageChannel, modmailThreadId, messageFromUser, member, true);
} else {
log.warn("Closing mod mail thread {}, because it seems the channel {} in server {} got deleted.", modmailThreadId, channelId, serverId);
// in this case there was no text channel on the server associated with the mod mail thread
@@ -464,14 +452,14 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* This message takes a received {@link Message} from a user, renders it to a new message to send and sends it to
* the appropriate {@link ModMailThread} channel, the returned promise only returns if the message was dealt with on the user
* side.
* @param textChannel The {@link TextChannel} in which the {@link ModMailThread} is being handled
* @param textChannel The {@link GuildMessageChannel} in which the {@link ModMailThread} is being handled
* @param modMailThreadId The id of the modmail thread to which the received {@link Message} is a reply to, can be null, if it is null, its the initial message
* @param messageFromUser The received message from the user
* @param member The {@link Member} instance from the user the thread is about. It is used as author
* @param modMailThreadExists Whether or not the modmail thread already exists and is persisted.
* @return A {@link CompletableFuture} which resolves when the post processing of the message is completed (adding read notification, and storing messageIDs)
*/
public CompletableFuture<Message> sendUserReply(TextChannel textChannel, Long modMailThreadId, Message messageFromUser, Member member, boolean modMailThreadExists) {
public CompletableFuture<Message> sendUserReply(GuildMessageChannel textChannel, Long modMailThreadId, Message messageFromUser, Member member, boolean modMailThreadExists) {
List<CompletableFuture<Member>> subscriberMemberFutures = new ArrayList<>();
if(modMailThreadExists) {
ModMailThread modMailThread = modMailThreadManagementService.getById(modMailThreadId);
@@ -551,7 +539,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* @param messageFromUser The {@link Message} object which was sent from the user
*/
@Transactional
public void postProcessSendMessages(TextChannel textChannel, Message messageInModMailThread, Message messageFromUser) {
public void postProcessSendMessages(GuildMessageChannel textChannel, Message messageInModMailThread, Message messageFromUser) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByChannelIdOptional(textChannel.getIdLong());
if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get();
@@ -793,7 +781,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
log.info("Modmail thread {} is empty. No messages to log.", modMailThreadId);
return CompletableFuture.completedFuture(new CompletableFutureList<>(new ArrayList<>()));
}
TextChannel channel = channelService.getTextChannelFromServer(serverId, modMailThreadId);
GuildMessageChannel channel = channelService.getMessageChannelFromServer(serverId, modMailThreadId);
ClosingProgressModel progressModel = ClosingProgressModel
.builder()
.loggedMessages(0)

View File

@@ -5,7 +5,6 @@ import dev.sheldan.abstracto.modmail.model.template.ModMailCategoryActionModel;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Category;
/**
* This represents both an instance of a {@link DelayedActionConfig} used to be executed in the
@@ -19,7 +18,6 @@ import net.dv8tion.jda.api.entities.Category;
public class ModMailCategoryDelayedActionConfig implements DelayedActionConfig {
private Long serverId;
private Long categoryId;
private Category category;
@Override
public String getTemplateName() {
@@ -30,7 +28,7 @@ public class ModMailCategoryDelayedActionConfig implements DelayedActionConfig {
public Object getTemplateModel() {
return ModMailCategoryActionModel
.builder()
.category(this.category)
.serverId(serverId)
.categoryId(categoryId)
.build();
}

View File

@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.core.models.AServerChannelUserId;
import dev.sheldan.abstracto.core.models.FeatureValidationResult;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.GuildService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
@@ -82,7 +83,10 @@ public class ModMailCategorySetupBean implements ModMailCategorySetup {
Long categoryId = configService.getLongValue(ModMailThreadServiceBean.MODMAIL_CATEGORY, user.getGuildId());
log.debug("Previous modmail category exists for server {}. Loading value {}.", guild.getId(), categoryId);
Category category = guild.getCategoryById(categoryId);
model.setCategory(category);
if(category != null) {
model.setCategoryId(category.getIdLong());
}
model.setServerId(user.getGuildId());
}
log.info("Executing mod mail category setup for server {}.", user.getGuildId());
String messageText = templateService.renderTemplate(messageTemplateKey, model);
@@ -90,8 +94,8 @@ public class ModMailCategorySetupBean implements ModMailCategorySetup {
CompletableFuture<SetupStepResult> future = new CompletableFuture<>();
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(user.getGuildId(), user.getUserId());
Runnable finalAction = getTimeoutRunnable(user.getGuildId(), user.getChannelId());
Consumer<MessageReceivedEvent> configAction = (MessageReceivedEvent event) -> {
Consumer<MessageReceivedModel> finalAction = getTimeoutConsumer(user.getGuildId(), user.getChannelId());
Consumer<MessageReceivedModel> configAction = (MessageReceivedModel event) -> {
try {
SetupStepResult result;
@@ -113,10 +117,14 @@ public class ModMailCategorySetupBean implements ModMailCategorySetup {
ModMailCategoryDelayedActionConfig build = ModMailCategoryDelayedActionConfig
.builder()
.serverId(user.getGuildId())
.category(guild.getCategoryById(categoryId))
.categoryId(categoryId)
.build();
List<DelayedActionConfig> delayedSteps = Arrays.asList(build);
DelayedActionConfigContainer container = DelayedActionConfigContainer
.builder()
.type(build.getClass())
.object(build)
.build();
List<DelayedActionConfigContainer> delayedSteps = Arrays.asList(container);
result = SetupStepResult
.builder()
.result(SetupStepResultType.SUCCESS)
@@ -135,12 +143,12 @@ public class ModMailCategorySetupBean implements ModMailCategorySetup {
future.completeExceptionally(new SetupStepException(e));
}
};
interactiveService.createMessageWithResponse(messageText, aUserInAServer, channel, parameter.getPreviousMessageId(), configAction, finalAction);
interactiveService.createMessageWithResponse(messageText, aUserInAServer, channel, configAction, finalAction);
return future;
}
protected Runnable getTimeoutRunnable(Long serverId, Long channelId) {
return () -> interactiveUtils.sendTimeoutMessage(serverId, channelId);
protected Consumer<MessageReceivedModel> getTimeoutConsumer(Long serverId, Long channelId) {
return (MessageReceivedModel) -> interactiveUtils.sendTimeoutMessage(serverId, channelId);
}
protected boolean checkForExit(Message message) {

View File

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

View File

@@ -1,24 +0,0 @@
package dev.sheldan.abstracto.modmail.model.dto;
import dev.sheldan.abstracto.core.models.FullGuild;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
/**
* Used when the user shares multiple servers with the bot and needs to determine for which server the user
* wants to open a mod mail thread, this is done by reacting to the prompt with the proper emote.
*/
@Getter
@Setter
@Builder
public class ServerChoice {
/**
* The possible guild to open a mod mail thread for
*/
private FullGuild guild;
/**
* The unicode emote used in the prompt to identify this choice
*/
private String reactionEmote;
}

View File

@@ -0,0 +1,18 @@
package dev.sheldan.abstracto.modmail.model.dto;
import dev.sheldan.abstracto.modmail.model.template.ServerChoice;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class ServerChoicePayload {
private Long serverId;
public static ServerChoicePayload fromServerChoice(ServerChoice choice) {
return ServerChoicePayload
.builder()
.serverId(choice.getServerId())
.build();
}
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.abstracto.modmail.model.dto;
import dev.sheldan.abstracto.core.models.template.button.ButtonPayload;
import dev.sheldan.abstracto.modmail.model.template.ServerChoices;
import lombok.Builder;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
@Getter
@Builder
public class ServiceChoicesPayload implements ButtonPayload {
private Map<String, ServerChoicePayload> choices;
private Long userId;
private Long messageId;
public static ServiceChoicesPayload fromServerChoices(ServerChoices choices) {
Map<String, ServerChoicePayload> newChoices = new HashMap<>();
choices.getCommonGuilds().forEach((s, choice) -> newChoices.put(s, ServerChoicePayload.fromServerChoice(choice)));
return ServiceChoicesPayload
.builder()
.userId(choices.getUserId())
.messageId(choices.getMessageId())
.choices(newChoices)
.build();
}
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.modmail.model.template;
import dev.sheldan.abstracto.core.utils.ChannelUtils;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@@ -14,6 +15,10 @@ import net.dv8tion.jda.api.entities.Category;
@Setter
@Builder
public class ModMailCategoryActionModel {
private Category category;
private Long serverId;
private Long categoryId;
public String getCategoryAsMention() {
return ChannelUtils.getAsMention(this.getCategoryId());
}
}

View File

@@ -1,12 +1,9 @@
package dev.sheldan.abstracto.modmail.model.template;
import dev.sheldan.abstracto.modmail.model.dto.ServerChoice;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
/**
* Container model used to define all the possible {@link ServerChoice} which are presented when the initial message is
* sent to the bot
@@ -15,9 +12,5 @@ import java.util.List;
@Setter
@Builder
public class ModMailServerChooserModel {
/**
* A list of {@link ServerChoice} which contains the common servers of the user and the bot, but only those
* in which the mod mail feature is currently enabled
*/
private List<ServerChoice> commonGuilds;
private ServerChoices choices;
}

View File

@@ -0,0 +1,11 @@
package dev.sheldan.abstracto.modmail.model.template;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class ServerChoice {
private String serverName;
private Long serverId;
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.modmail.model.template;
import lombok.Builder;
import lombok.Getter;
import java.util.Map;
@Getter
@Builder
public class ServerChoices {
private Map<String, ServerChoice> commonGuilds;
private Long userId;
private Long messageId;
}

View File

@@ -1,9 +1,9 @@
package dev.sheldan.abstracto.modmail.model.template;
import dev.sheldan.abstracto.core.utils.ChannelUtils;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Category;
/**
* Model which is used when setting up the mod mail feature. The category property will be used when there is already a category
@@ -13,5 +13,10 @@ import net.dv8tion.jda.api.entities.Category;
@Setter
@Builder
public class SetupModMailCategoryMessageModel {
private Category category;
private Long serverId;
private Long categoryId;
public String getCategoryAsMention() {
return ChannelUtils.getAsMention(this.getCategoryId());
}
}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.3.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -19,10 +19,7 @@ import dev.sheldan.abstracto.remind.service.management.ReminderManagementService
import dev.sheldan.abstracto.scheduling.model.JobParameters;
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@@ -133,7 +130,7 @@ public class RemindServiceBean implements ReminderService {
reminderId, channel.getId(), server.getId(), reminderToRemindFor.getRemindedUser().getUserReference().getId());
Optional<Guild> guildToAnswerIn = guildService.getGuildByIdOptional(server.getId());
if(guildToAnswerIn.isPresent()) {
Optional<TextChannel> channelToAnswerIn = channelService.getTextChannelFromServerOptional(server.getId(), channel.getId());
Optional<GuildMessageChannel> channelToAnswerIn = channelService.getMessageChannelFromServerOptional(server.getId(), channel.getId());
// only send the message if the channel still exists, if not, only set the reminder to reminded.
if(channelToAnswerIn.isPresent()) {
memberService.getMemberInServerAsync(server.getId(), reminderToRemindFor.getRemindedUser().getUserReference().getId()).thenAccept(member ->
@@ -150,7 +147,7 @@ public class RemindServiceBean implements ReminderService {
}
@Transactional
public CompletableFuture<Void> sendReminderText(Long reminderId, TextChannel channelToAnswerIn, Member member) {
public CompletableFuture<Void> sendReminderText(Long reminderId, GuildMessageChannel channelToAnswerIn, Member member) {
Reminder reminder = reminderManagementService.loadReminder(reminderId);
log.debug("Sending remind message for reminder {} to user user {} in server {}.", reminderId, member.getIdLong(), member.getGuild().getIdLong());
ExecutedReminderModel build = ExecutedReminderModel

View File

@@ -16,10 +16,7 @@ import dev.sheldan.abstracto.scheduling.model.JobParameters;
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.*;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -73,7 +70,7 @@ public class RemindServiceBeanTest {
private Message message;
@Mock
private TextChannel channel;
private GuildMessageChannel channel;
@Mock
private ScheduledExecutorService instantReminderScheduler;
@@ -155,7 +152,7 @@ public class RemindServiceBeanTest {
when(reminderManagementService.loadReminder(REMINDER_ID)).thenReturn(remindedReminder);
Guild guildMock = Mockito.mock(Guild.class);
when(guildService.getGuildByIdOptional(SERVER_ID)).thenReturn(Optional.of(guildMock));
when(channelService.getTextChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.of(channel));
when(channelService.getMessageChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.of(channel));
Member mockedMember = Mockito.mock(Member.class);
when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(mockedMember));
testUnit.executeReminder(REMINDER_ID);
@@ -174,7 +171,7 @@ public class RemindServiceBeanTest {
when(guildService.getGuildByIdOptional(SERVER_ID)).thenReturn(Optional.of(guildMock));
when(aUserInAServer.getUserReference()).thenReturn(user);
when(remindedReminder.getRemindedUser()).thenReturn(aUserInAServer);
when(channelService.getTextChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.empty());
when(channelService.getMessageChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.empty());
testUnit.executeReminder(REMINDER_ID);
verify(reminderManagementService, times(1)).setReminded(remindedReminder);
verify(self, times(0)).sendReminderText(anyLong(), any(), any(Member.class));

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>remind</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -26,7 +26,7 @@ public class RepostCheckChannelModelConverter {
FullChannel
.builder()
.channel(channel)
.serverChannel(channelService.getTextChannelFromServerNullable(guild, channel.getId()))
.serverChannel(channelService.getMessageChannelFromServerNullable(guild, channel.getId()))
.build()
).collect(Collectors.toList());
repostCheckChannelGroups.add(

View File

@@ -39,7 +39,7 @@ public class RepostMessageReceivedListener implements AsyncMessageReceivedListen
if(!message.isFromGuild() || message.isWebhookMessage() || message.getType().isSystem()) {
return DefaultListenerResult.IGNORED;
}
AChannel channel = channelManagementService.loadChannel(message.getTextChannel().getIdLong());
AChannel channel = channelManagementService.loadChannel(message.getChannel().getIdLong());
if(repostCheckChannelService.duplicateCheckEnabledForChannel(channel)) {
repostService.processMessageAttachmentRepostCheck(message);
List<MessageEmbed> imageEmbeds = message.getEmbeds().stream().filter(messageEmbed -> messageEmbed.getType().equals(EmbedType.IMAGE)).collect(Collectors.toList());

View File

@@ -62,11 +62,11 @@ public class RepostCheckChannelModelConverterTest {
AChannel channel1 = Mockito.mock(AChannel.class);
when(channel1.getId()).thenReturn(channelId1);
TextChannel textChannel = Mockito.mock(TextChannel.class);
when(channelService.getTextChannelFromServerNullable(guild, channelId1)).thenReturn(textChannel);
when(channelService.getMessageChannelFromServerNullable(guild, channelId1)).thenReturn(textChannel);
Long channelId2 = 2L;
AChannel channel2 = Mockito.mock(AChannel.class);
when(channel2.getId()).thenReturn(channelId2);
when(channelService.getTextChannelFromServerNullable(guild, channelId2)).thenReturn(null);
when(channelService.getMessageChannelFromServerNullable(guild, channelId2)).thenReturn(null);
when(group.getChannels()).thenReturn(Arrays.asList(channel1, channel2));
RepostCheckChannelsModel model = testUnit.fromRepostCheckChannelGroups(Arrays.asList(element), guild);
Assert.assertEquals(1, model.getRepostCheckChannelGroups().size());

View File

@@ -42,7 +42,7 @@ public class RepostMessageReceivedListenerTest {
private MessageReceivedModel model;
@Mock
private TextChannel textChannel;
private GuildMessageChannel textChannel;
@Captor
private ArgumentCaptor<List<MessageEmbed>> embedListCaptor;
@@ -89,7 +89,7 @@ public class RepostMessageReceivedListenerTest {
}
private void setupRepostCheckEnabled(boolean b) {
when(message.getTextChannel()).thenReturn(textChannel);
when(message.getChannel()).thenReturn(textChannel);
when(message.isFromGuild()).thenReturn(true);
when(message.isWebhookMessage()).thenReturn(false);
MessageType type = MessageType.DEFAULT;

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>repost-detection</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -25,10 +25,7 @@ import dev.sheldan.abstracto.starboard.model.template.*;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -159,7 +156,7 @@ public class StarboardServiceBean implements StarboardService {
}
private StarboardPostModel createStarboardModel(CachedMessage message, Integer starCount, net.dv8tion.jda.api.entities.User user) {
Optional<TextChannel> channel = channelService.getTextChannelFromServerOptional(message.getServerId(), message.getChannelId());
Optional<GuildMessageChannel> channel = channelService.getMessageChannelFromServerOptional(message.getServerId(), message.getChannelId());
Optional<Guild> guild = guildService.getGuildByIdOptional(message.getServerId());
String starLevelEmote = getAppropriateEmote(message.getServerId(), starCount);
return StarboardPostModel

View File

@@ -97,7 +97,7 @@ public class StarboardServiceBeanTest {
private Message sendPost;
@Mock
private TextChannel mockedTextChannel;
private GuildMessageChannel mockedTextChannel;
@Mock
private User starredJdaUser;
@@ -156,7 +156,7 @@ public class StarboardServiceBeanTest {
when(message.getServerId()).thenReturn(SERVER_ID);
when(message.getChannelId()).thenReturn(CHANNEL_ID);
when(userService.retrieveUserForId(STARRED_USER_ID)).thenReturn(CompletableFuture.completedFuture(starredJdaUser));
when(channelService.getTextChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.of(mockedTextChannel));
when(channelService.getMessageChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.of(mockedTextChannel));
when(guildService.getGuildByIdOptional(SERVER_ID)).thenReturn(Optional.of(guild));
SystemConfigProperty config = Mockito.mock(SystemConfigProperty.class);
Long defaultValue = 3L;

View File

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

View File

@@ -5,7 +5,7 @@ import dev.sheldan.abstracto.core.models.context.ServerContext;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.User;
@Getter
@@ -13,7 +13,7 @@ import net.dv8tion.jda.api.entities.User;
@SuperBuilder
public class StarboardPostModel extends ServerContext {
private User author;
private TextChannel channel;
private GuildMessageChannel channel;
private Long sourceChannelId;
private CachedMessage message;
private Integer starCount;

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.3.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -12,8 +12,8 @@ import dev.sheldan.abstracto.suggestion.model.template.SuggestionButtonPayload;
import dev.sheldan.abstracto.suggestion.service.SuggestionServiceBean;
import dev.sheldan.abstracto.suggestion.service.SuggestionVoteService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.interaction.ButtonClickEvent;
import net.dv8tion.jda.api.interactions.components.ButtonInteraction;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.interactions.components.buttons.ButtonInteraction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -33,7 +33,7 @@ public class SuggestionButtonVoteClickedListener implements ButtonClickedListene
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
ButtonClickEvent event = model.getEvent();
ButtonInteractionEvent event = model.getEvent();
SuggestionButtonPayload payload = (SuggestionButtonPayload) model.getDeserializedPayload();
suggestionVoteService.upsertSuggestionVote(event.getMember(), payload.getDecision(), payload.getSuggestionId());
ButtonInteraction buttonInteraction = model.getEvent().getInteraction();
@@ -58,7 +58,7 @@ public class SuggestionButtonVoteClickedListener implements ButtonClickedListene
@Override
public Boolean handlesEvent(ButtonClickedListenerModel model) {
return SuggestionServiceBean.SUGGESTION_VOTE_ORIGIN.equals(model.getOrigin());
return SuggestionServiceBean.SUGGESTION_VOTE_ORIGIN.equals(model.getOrigin()) && model.getEvent().isFromGuild();
}
@Override

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>suggestion</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</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.14-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

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