diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/mention/AllowMention.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/mention/AllowMention.java new file mode 100644 index 000000000..4478c9d8b --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/mention/AllowMention.java @@ -0,0 +1,53 @@ +package dev.sheldan.abstracto.core.commands.config.mention; + +import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; +import dev.sheldan.abstracto.core.command.config.CommandConfiguration; +import dev.sheldan.abstracto.core.command.config.HelpInfo; +import dev.sheldan.abstracto.core.command.config.Parameter; +import dev.sheldan.abstracto.core.command.config.features.CoreFeatures; +import dev.sheldan.abstracto.core.command.execution.CommandContext; +import dev.sheldan.abstracto.core.command.execution.CommandResult; +import dev.sheldan.abstracto.core.commands.config.ConfigModuleInterface; +import dev.sheldan.abstracto.core.config.FeatureEnum; +import dev.sheldan.abstracto.core.service.AllowedMentionService; +import net.dv8tion.jda.api.entities.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; + +@Component +public class AllowMention extends AbstractConditionableCommand { + @Autowired + private AllowedMentionService allowedMentionService; + + @Override + public CommandResult execute(CommandContext commandContext) { + String mentionTypeInput = (String) commandContext.getParameters().getParameters().get(0); + Message.MentionType mentionType = allowedMentionService.getMentionTypeFromString(mentionTypeInput); + allowedMentionService.allowMentionForServer(mentionType, commandContext.getGuild().getIdLong()); + return CommandResult.fromSuccess(); + } + + @Override + public CommandConfiguration getConfiguration() { + Parameter mentionTypeParameter = Parameter.builder().name("mentionType").type(String.class).templated(true).build(); + List parameters = Arrays.asList(mentionTypeParameter); + HelpInfo helpInfo = HelpInfo.builder().templated(true).build(); + return CommandConfiguration.builder() + .name("allowMention") + .module(ConfigModuleInterface.CONFIG) + .parameters(parameters) + .templated(true) + .supportsEmbedException(true) + .help(helpInfo) + .causesReaction(true) + .build(); + } + + @Override + public FeatureEnum getFeature() { + return CoreFeatures.CORE_FEATURE; + } +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/mention/DisallowMention.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/mention/DisallowMention.java new file mode 100644 index 000000000..8a6c2187d --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/config/mention/DisallowMention.java @@ -0,0 +1,55 @@ +package dev.sheldan.abstracto.core.commands.config.mention; + +import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; +import dev.sheldan.abstracto.core.command.config.CommandConfiguration; +import dev.sheldan.abstracto.core.command.config.HelpInfo; +import dev.sheldan.abstracto.core.command.config.Parameter; +import dev.sheldan.abstracto.core.command.config.features.CoreFeatures; +import dev.sheldan.abstracto.core.command.execution.CommandContext; +import dev.sheldan.abstracto.core.command.execution.CommandResult; +import dev.sheldan.abstracto.core.commands.config.ConfigModuleInterface; +import dev.sheldan.abstracto.core.config.FeatureEnum; +import dev.sheldan.abstracto.core.service.AllowedMentionService; +import net.dv8tion.jda.api.entities.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; + +@Component +public class DisallowMention extends AbstractConditionableCommand { + + @Autowired + private AllowedMentionService allowedMentionService; + + @Override + public CommandResult execute(CommandContext commandContext) { + String mentionTypeInput = (String) commandContext.getParameters().getParameters().get(0); + Message.MentionType mentionType = allowedMentionService.getMentionTypeFromString(mentionTypeInput); + allowedMentionService.disAllowMentionForServer(mentionType, commandContext.getGuild().getIdLong()); + return CommandResult.fromSuccess(); + } + + @Override + public CommandConfiguration getConfiguration() { + Parameter mentionTypeParameter = Parameter.builder().name("mentionType").type(String.class).templated(true).build(); + List parameters = Arrays.asList(mentionTypeParameter); + HelpInfo helpInfo = HelpInfo.builder().templated(true).build(); + return CommandConfiguration.builder() + .name("disallowMention") + .module(ConfigModuleInterface.CONFIG) + .parameters(parameters) + .templated(true) + .supportsEmbedException(true) + .help(helpInfo) + .causesReaction(true) + .build(); + } + + @Override + public FeatureEnum getFeature() { + return CoreFeatures.CORE_FEATURE; + } + +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/utility/SetEmote.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/utility/SetEmote.java index 942abda63..54db3ea92 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/utility/SetEmote.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/commands/utility/SetEmote.java @@ -1,6 +1,5 @@ package dev.sheldan.abstracto.core.commands.utility; -import dev.sheldan.abstracto.core.command.UtilityModuleInterface; import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; import dev.sheldan.abstracto.core.command.config.CommandConfiguration; import dev.sheldan.abstracto.core.command.config.HelpInfo; @@ -10,6 +9,7 @@ import dev.sheldan.abstracto.core.command.config.features.CoreFeatures; import dev.sheldan.abstracto.core.command.config.validator.MaxStringLengthValidator; import dev.sheldan.abstracto.core.command.execution.CommandContext; import dev.sheldan.abstracto.core.command.execution.CommandResult; +import dev.sheldan.abstracto.core.commands.config.ConfigModuleInterface; import dev.sheldan.abstracto.core.config.FeatureEnum; import dev.sheldan.abstracto.core.models.database.AEmote; import dev.sheldan.abstracto.core.service.EmoteService; @@ -47,7 +47,7 @@ public class SetEmote extends AbstractConditionableCommand { HelpInfo helpInfo = HelpInfo.builder().templated(true).build(); return CommandConfiguration.builder() .name("setEmote") - .module(UtilityModuleInterface.UTILITY) + .module(ConfigModuleInterface.CONFIG) .parameters(parameters) .supportsEmbedException(true) .help(helpInfo) diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/config/AllowedMentionConfig.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/config/AllowedMentionConfig.java new file mode 100644 index 000000000..1e133a8dd --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/config/AllowedMentionConfig.java @@ -0,0 +1,16 @@ +package dev.sheldan.abstracto.core.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Getter +@Setter +@Configuration +@ConfigurationProperties(prefix = "abstracto.allowedmention") +public class AllowedMentionConfig { + private Boolean everyone; + private Boolean role; + private Boolean user; +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/AllowedMentionRepository.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/AllowedMentionRepository.java new file mode 100644 index 000000000..ecbe06dd0 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/AllowedMentionRepository.java @@ -0,0 +1,15 @@ +package dev.sheldan.abstracto.core.repository; + +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.models.database.AllowedMention; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + + +@Repository +public interface AllowedMentionRepository extends JpaRepository { + Optional findByServer(AServer server); + +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/ChannelGroupRepository.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/ChannelGroupRepository.java index be770360a..2b7d0386c 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/ChannelGroupRepository.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/repository/ChannelGroupRepository.java @@ -5,11 +5,13 @@ import dev.sheldan.abstracto.core.models.database.AChannelGroup; import dev.sheldan.abstracto.core.models.database.AServer; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.QueryHints; +import org.springframework.stereotype.Repository; import javax.persistence.QueryHint; import java.util.List; import java.util.Optional; +@Repository public interface ChannelGroupRepository extends JpaRepository { @QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true")) diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/AllowedMentionServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/AllowedMentionServiceBean.java new file mode 100644 index 000000000..bf9175333 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/AllowedMentionServiceBean.java @@ -0,0 +1,125 @@ +package dev.sheldan.abstracto.core.service; + +import dev.sheldan.abstracto.core.config.AllowedMentionConfig; +import dev.sheldan.abstracto.core.models.database.AllowedMention; +import dev.sheldan.abstracto.core.exception.UnknownMentionTypeException; +import dev.sheldan.abstracto.core.service.management.AllowedMentionManagementService; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; + +@Component +@Slf4j +public class AllowedMentionServiceBean implements AllowedMentionService { + + public static final String EVERYONE_MENTION_KEY = "everyone"; + public static final String ROLE_MENTION_KEY = "role"; + public static final String USER_MENTION_KEY = "user"; + + @Autowired + private AllowedMentionConfig allowedMentionConfig; + + @Autowired + private AllowedMentionManagementService allowedMentionManagementService; + + private final HashMap ALL_MENTION_TYPES = new HashMap<>(); + + @Override + public boolean allMentionsAllowed(Long serverId) { + return getEffectiveAllowedMention(serverId).allAllowed(); + } + + @Override + public List getAllowedMentionTypesForServer(Long serverId) { + AllowedMention allowedMention = getEffectiveAllowedMention(serverId); + return mapAllowedMentionToMentionType(allowedMention); + } + + @Override + public void allowMentionForServer(Message.MentionType mentionType, Long serverId) { + log.info("Allowing mention {} for server {}.", mentionType, serverId); + setOrInitializeAllowedMention(mentionType, serverId, true); + } + + @Override + public void disAllowMentionForServer(Message.MentionType mentionType, Long serverId) { + log.info("Disallowing mention {} for server {}.", mentionType, serverId); + setOrInitializeAllowedMention(mentionType, serverId, false); + } + + @Override + public AllowedMention getDefaultAllowedMention() { + return AllowedMention + .builder() + .everyone(allowedMentionConfig.getEveryone()) + .role(allowedMentionConfig.getRole()) + .user(allowedMentionConfig.getUser()) + .build(); + } + + @Override + public AllowedMention getEffectiveAllowedMention(Long serverId) { + Optional customAllowMentions = allowedMentionManagementService.getCustomAllowedMentionFor(serverId); + return customAllowMentions.orElseGet(this::getDefaultAllowedMention); + } + + @Override + public Message.MentionType getMentionTypeFromString(String input) { + input = input.toLowerCase(); + if (ALL_MENTION_TYPES.containsKey(input)) { + return ALL_MENTION_TYPES.get(input); + } + throw new UnknownMentionTypeException(); + } + + private List mapAllowedMentionToMentionType(AllowedMention allowedMention) { + // if all are allowed, we dont want to restrict it + if(allowedMention.allAllowed()) { + return null; + } + List types = new ArrayList<>(); + if(allowedMention.getEveryone()) { + types.add(Message.MentionType.EVERYONE); + } + if(allowedMention.getRole()) { + types.add(Message.MentionType.ROLE); + } + if(allowedMention.getUser()) { + types.add(Message.MentionType.USER); + } + return types; + } + + private void setOrInitializeAllowedMention(Message.MentionType mentionType, Long serverId, boolean initialMentionValue) { + Optional customAllowedMentionOptional = allowedMentionManagementService.getCustomAllowedMentionFor(serverId); + AllowedMention customAllowedMention = customAllowedMentionOptional.orElseGet(this::getDefaultAllowedMention); + switch (mentionType) { + case EVERYONE: + customAllowedMention.setEveryone(initialMentionValue); + break; + case ROLE: + customAllowedMention.setRole(initialMentionValue); + break; + case USER: + customAllowedMention.setUser(initialMentionValue); + break; + } + if (!customAllowedMentionOptional.isPresent()) { + allowedMentionManagementService.createCustomAllowedMention(serverId, customAllowedMention); + } + } + + @PostConstruct + public void postConstruct() { + ALL_MENTION_TYPES.put(EVERYONE_MENTION_KEY, Message.MentionType.EVERYONE); + ALL_MENTION_TYPES.put(ROLE_MENTION_KEY, Message.MentionType.ROLE); + ALL_MENTION_TYPES.put(USER_MENTION_KEY, Message.MentionType.USER); + } +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java index 4115bb382..47eed9306 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java @@ -44,6 +44,9 @@ public class ChannelServiceBean implements ChannelService { @Autowired private TemplateService templateService; + @Autowired + private AllowedMentionService allowedMentionService; + @Autowired private MetricService metricService; @@ -119,14 +122,21 @@ public class ChannelServiceBean implements ChannelService { log.trace("Sending message {} from channel {} and server {} to channel {}.", message.getId(), message.getChannel().getId(), message.getGuild().getId(), channel.getId()); metricService.incrementCounter(MESSAGE_SEND_METRIC); - return channel.sendMessage(message).submit(); + return channel.sendMessage(message).allowedMentions(getAllowedMentionsFor(channel)).submit(); + } + + private List getAllowedMentionsFor(MessageChannel channel) { + if(channel instanceof GuildChannel) { + return allowedMentionService.getAllowedMentionTypesForServer(((GuildChannel) channel).getGuild().getIdLong()); + } + return null; } @Override public CompletableFuture sendTextToChannel(String text, MessageChannel channel) { log.trace("Sending text to channel {}.", channel.getId()); metricService.incrementCounter(MESSAGE_SEND_METRIC); - return channel.sendMessage(text).submit(); + return channel.sendMessage(text).allowedMentions(getAllowedMentionsFor(channel)).submit(); } @Override @@ -155,7 +165,7 @@ public class ChannelServiceBean implements ChannelService { @Override public MessageAction sendEmbedToChannelInComplete(MessageEmbed embed, MessageChannel channel) { metricService.incrementCounter(MESSAGE_SEND_METRIC); - return channel.sendMessage(embed); + return channel.sendMessage(embed).allowedMentions(getAllowedMentionsFor(channel)); } @Override @@ -213,8 +223,9 @@ public class ChannelServiceBean implements ChannelService { } } allMessageActions.add(0, firstMessageAction); + List allowedMentions = getAllowedMentionsFor(textChannel); allMessageActions.forEach(messageAction -> - futures.add(messageAction.submit()) + futures.add(messageAction.allowedMentions(allowedMentions).submit()) ); return futures; } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java index a6fce0464..feb1718d6 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupServiceBean.java @@ -56,7 +56,7 @@ public class StartupServiceBean implements Startup { } @Override - @Transactional(isolation = Isolation.SERIALIZABLE) + @Transactional public void synchronize() { log.info("Synchronizing servers."); synchronizeServers(); @@ -70,7 +70,7 @@ public class StartupServiceBean implements Startup { availableServers.forEach(aLong -> { AServer newAServer = serverManagementService.loadOrCreate(aLong); Guild newGuild = instance.getGuildById(aLong); - log.trace("Synchronizing server: {}", aLong); + log.info("Synchronizing server: {}", aLong); if(newGuild != null){ synchronizeRolesOf(newGuild, newAServer); synchronizeChannelsOf(newGuild, newAServer); @@ -91,7 +91,6 @@ public class StartupServiceBean implements Startup { Set newRoles = SetUtils.difference(availableRoles, knownRolesId); newRoles.forEach(aLong -> { roleManagementService.createRole(aLong, existingAServer); - log.trace("Adding new role: {}", aLong); }); } @@ -103,7 +102,6 @@ public class StartupServiceBean implements Startup { Set newChannels = SetUtils.difference(existingChannelsIds, knownChannelsIds); newChannels.forEach(aLong -> { GuildChannel channel1 = available.stream().filter(channel -> channel.getIdLong() == aLong).findFirst().get(); - log.trace("Adding new channel: {}", aLong); AChannelType type = AChannelType.getAChannelType(channel1.getType()); channelManagementService.createChannel(channel1.getIdLong(), type, existingServer); }); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/AllowedMentionManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/AllowedMentionManagementServiceBean.java new file mode 100644 index 000000000..f54b17f69 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/AllowedMentionManagementServiceBean.java @@ -0,0 +1,55 @@ +package dev.sheldan.abstracto.core.service.management; + +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.models.database.AllowedMention; +import dev.sheldan.abstracto.core.repository.AllowedMentionRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +@Slf4j +public class AllowedMentionManagementServiceBean implements AllowedMentionManagementService { + + @Autowired + private AllowedMentionRepository allowedMentionRepository; + + @Autowired + private ServerManagementService serverManagementServiceBean; + + @Override + public Optional getCustomAllowedMentionFor(Long serverId) { + return allowedMentionRepository.findById(serverId); + } + + @Override + public Optional getCustomAllowedMentionFor(AServer server) { + return allowedMentionRepository.findByServer(server); + } + + @Override + public boolean hasCustomAllowedMention(Long serverId) { + return getCustomAllowedMentionFor(serverId).isPresent(); + } + + @Override + public AllowedMention createCustomAllowedMention(Long serverId, AllowedMention base) { + log.info("Creating custom allowed mention for server {} based on {}.", serverId, base); + AServer server = serverManagementServiceBean.loadOrCreate(serverId); + AllowedMention allowedMention = AllowedMention + .builder() + .everyone(base.getEveryone()) + .role(base.getRole()) + .user(base.getUser()) + .server(server).build(); + allowedMentionRepository.save(allowedMention); + return allowedMention; + } + + @Override + public void deleteCustomAllowedMention(Long serverId) { + allowedMentionRepository.deleteById(serverId); + } +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java index 1446f2d49..318ec69a7 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/RoleManagementServiceBean.java @@ -25,7 +25,6 @@ public class RoleManagementServiceBean implements RoleManagementService { .server(server) .deleted(false) .build(); - server.getRoles().add(build); log.info("Creating role {} in server {}.", id, server.getId()); return repository.save(build); } diff --git a/abstracto-application/core/core-impl/src/main/resources/abstracto.properties b/abstracto-application/core/core-impl/src/main/resources/abstracto.properties index 56333e6a5..6ac6330e9 100644 --- a/abstracto-application/core/core-impl/src/main/resources/abstracto.properties +++ b/abstracto-application/core/core-impl/src/main/resources/abstracto.properties @@ -1,4 +1,9 @@ abstracto.startup.synchronize=true abstracto.eventWaiter.threads=10 -server.port=8080 \ No newline at end of file +server.port=8080 + + +abstracto.allowedmention.everyone=false +abstracto.allowedmention.role=true +abstracto.allowedmention.user=true \ No newline at end of file diff --git a/abstracto-application/core/core-impl/src/main/resources/migrations/1.0-core/core-seedData/command.xml b/abstracto-application/core/core-impl/src/main/resources/migrations/1.0-core/core-seedData/command.xml index 6e3eab36c..351feba3b 100644 --- a/abstracto-application/core/core-impl/src/main/resources/migrations/1.0-core/core-seedData/command.xml +++ b/abstracto-application/core/core-impl/src/main/resources/migrations/1.0-core/core-seedData/command.xml @@ -33,12 +33,6 @@ - - - - - - @@ -187,5 +181,23 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/abstracto-application/core/core-impl/src/main/resources/migrations/1.0-core/core-tables/allowed_mention.xml b/abstracto-application/core/core-impl/src/main/resources/migrations/1.0-core/core-tables/allowed_mention.xml new file mode 100644 index 000000000..90e0a5883 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/resources/migrations/1.0-core/core-tables/allowed_mention.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/abstracto-application/core/core-impl/src/main/resources/migrations/1.0-core/core-tables/tables.xml b/abstracto-application/core/core-impl/src/main/resources/migrations/1.0-core/core-tables/tables.xml index 70565e44d..f5719143b 100644 --- a/abstracto-application/core/core-impl/src/main/resources/migrations/1.0-core/core-tables/tables.xml +++ b/abstracto-application/core/core-impl/src/main/resources/migrations/1.0-core/core-tables/tables.xml @@ -27,4 +27,5 @@ + \ No newline at end of file diff --git a/abstracto-application/core/core-impl/src/test/java/dev/sheldan/abstracto/core/service/AllowedMentionServiceBeanTest.java b/abstracto-application/core/core-impl/src/test/java/dev/sheldan/abstracto/core/service/AllowedMentionServiceBeanTest.java new file mode 100644 index 000000000..9049a92fe --- /dev/null +++ b/abstracto-application/core/core-impl/src/test/java/dev/sheldan/abstracto/core/service/AllowedMentionServiceBeanTest.java @@ -0,0 +1,169 @@ +package dev.sheldan.abstracto.core.service; + +import dev.sheldan.abstracto.core.config.AllowedMentionConfig; +import dev.sheldan.abstracto.core.exception.UnknownMentionTypeException; +import dev.sheldan.abstracto.core.models.database.AllowedMention; +import dev.sheldan.abstracto.core.service.management.AllowedMentionManagementService; +import net.dv8tion.jda.api.entities.Message; +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.junit.MockitoJUnitRunner; + +import java.util.List; +import java.util.Optional; + +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class AllowedMentionServiceBeanTest { + + @InjectMocks + private AllowedMentionServiceBean testUnit; + + @Mock + private AllowedMentionConfig allowedMentionConfig; + + @Mock + private AllowedMentionManagementService allowedMentionManagementService; + + @Mock + private AllowedMention allowedMention; + + private static final Long SERVER_ID = 4L; + + @Before + public void setup() { + testUnit.postConstruct(); + } + + @Test + public void allMentionsAllowedDefaultAll() { + allDefaultConfigAllowed(); + when(allowedMentionManagementService.getCustomAllowedMentionFor(SERVER_ID)).thenReturn(Optional.empty()); + Assert.assertTrue(testUnit.allMentionsAllowed(SERVER_ID)); + } + + @Test + public void allMentionsAllowedDefaultNotAll() { + when(allowedMentionConfig.getEveryone()).thenReturn(true); + when(allowedMentionConfig.getRole()).thenReturn(false); + when(allowedMentionConfig.getUser()).thenReturn(true); + when(allowedMentionManagementService.getCustomAllowedMentionFor(SERVER_ID)).thenReturn(Optional.empty()); + Assert.assertFalse(testUnit.allMentionsAllowed(SERVER_ID)); + } + + @Test + public void allMentionsAllowedCustomAll() { + when(allowedMention.allAllowed()).thenReturn(true); + when(allowedMentionManagementService.getCustomAllowedMentionFor(SERVER_ID)).thenReturn(Optional.of(allowedMention)); + Assert.assertTrue(testUnit.allMentionsAllowed(SERVER_ID)); + } + + @Test + public void getAllowedMentionTypesForServerEmpty() { + allDefaultConfigAllowed(); + when(allowedMentionManagementService.getCustomAllowedMentionFor(SERVER_ID)).thenReturn(Optional.empty()); + List allowedMentions = testUnit.getAllowedMentionTypesForServer(SERVER_ID); + Assert.assertNull(allowedMentions); + } + + @Test + public void getAllowedMentionTypesAllAllowed() { + when(allowedMentionConfig.getEveryone()).thenReturn(true); + when(allowedMentionConfig.getRole()).thenReturn(false); + when(allowedMentionConfig.getUser()).thenReturn(false); + when(allowedMentionManagementService.getCustomAllowedMentionFor(SERVER_ID)).thenReturn(Optional.empty()); + List allowedMentions = testUnit.getAllowedMentionTypesForServer(SERVER_ID); + Assert.assertEquals(1, allowedMentions.size()); + Assert.assertEquals(Message.MentionType.EVERYONE, allowedMentions.get(0)); + } + + @Test + public void getAllowedMentionTypesAllDisallowed() { + when(allowedMentionConfig.getEveryone()).thenReturn(false); + when(allowedMentionConfig.getRole()).thenReturn(false); + when(allowedMentionConfig.getUser()).thenReturn(false); + when(allowedMentionManagementService.getCustomAllowedMentionFor(SERVER_ID)).thenReturn(Optional.empty()); + List allowedMentions = testUnit.getAllowedMentionTypesForServer(SERVER_ID); + Assert.assertEquals(0, allowedMentions.size()); + } + + @Test + public void allowMentionForServerWithExistingCustom() { + when(allowedMentionManagementService.getCustomAllowedMentionFor(SERVER_ID)).thenReturn(Optional.of(allowedMention)); + testUnit.allowMentionForServer(Message.MentionType.EVERYONE, SERVER_ID); + verify(allowedMention, times(1)).setEveryone(true); + } + + @Test + public void allowMentionForServerWithNewCustom() { + when(allowedMentionManagementService.getCustomAllowedMentionFor(SERVER_ID)).thenReturn(Optional.empty()); + testUnit.allowMentionForServer(Message.MentionType.EVERYONE, SERVER_ID); + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(AllowedMention.class); + verify(allowedMentionManagementService, times(1)).createCustomAllowedMention(eq(SERVER_ID), argumentCaptor.capture()); + AllowedMention creationArgument = argumentCaptor.getValue(); + Assert.assertTrue(creationArgument.getEveryone()); + } + + @Test + public void disAllowMentionForServer() { + when(allowedMentionManagementService.getCustomAllowedMentionFor(SERVER_ID)).thenReturn(Optional.empty()); + testUnit.disAllowMentionForServer(Message.MentionType.EVERYONE, SERVER_ID); + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(AllowedMention.class); + verify(allowedMentionManagementService, times(1)).createCustomAllowedMention(eq(SERVER_ID), argumentCaptor.capture()); + AllowedMention creationArgument = argumentCaptor.getValue(); + Assert.assertFalse(creationArgument.getEveryone()); + } + + @Test + public void getDefaultAllowedMention() { + when(allowedMentionConfig.getEveryone()).thenReturn(true); + AllowedMention defaultMention = testUnit.getDefaultAllowedMention(); + Assert.assertEquals(true, defaultMention.getEveryone()); + } + + @Test + public void getEffectiveAllowedMentionWithCustom() { + when(allowedMentionManagementService.getCustomAllowedMentionFor(SERVER_ID)).thenReturn(Optional.of(allowedMention)); + AllowedMention effectiveMention = testUnit.getEffectiveAllowedMention(SERVER_ID); + Assert.assertEquals(allowedMention, effectiveMention); + } + + @Test + public void getEffectiveAllowedMentionNoCustom() { + when(allowedMentionConfig.getEveryone()).thenReturn(true); + when(allowedMentionManagementService.getCustomAllowedMentionFor(SERVER_ID)).thenReturn(Optional.empty()); + AllowedMention effectiveMention = testUnit.getEffectiveAllowedMention(SERVER_ID); + Assert.assertNull(effectiveMention.getServer()); + Assert.assertTrue(effectiveMention.getEveryone()); + } + + @Test + public void getMentionTypeFromString() { + Message.MentionType result = testUnit.getMentionTypeFromString("everyone"); + Assert.assertEquals(Message.MentionType.EVERYONE, result); + } + + @Test + public void getMentionTypeFromStringIgnoreCase() { + Message.MentionType result = testUnit.getMentionTypeFromString("eVeRyOnE"); + Assert.assertEquals(Message.MentionType.EVERYONE, result); + } + + @Test(expected = UnknownMentionTypeException.class) + public void getUnknownMentionType() { + testUnit.getMentionTypeFromString("test"); + } + + private void allDefaultConfigAllowed() { + when(allowedMentionConfig.getEveryone()).thenReturn(true); + when(allowedMentionConfig.getRole()).thenReturn(true); + when(allowedMentionConfig.getUser()).thenReturn(true); + } + +} \ No newline at end of file diff --git a/abstracto-application/core/core-impl/src/test/java/dev/sheldan/abstracto/core/service/management/AllowedMentionManagementServiceBeanTest.java b/abstracto-application/core/core-impl/src/test/java/dev/sheldan/abstracto/core/service/management/AllowedMentionManagementServiceBeanTest.java new file mode 100644 index 000000000..f333a5413 --- /dev/null +++ b/abstracto-application/core/core-impl/src/test/java/dev/sheldan/abstracto/core/service/management/AllowedMentionManagementServiceBeanTest.java @@ -0,0 +1,82 @@ +package dev.sheldan.abstracto.core.service.management; + +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.models.database.AllowedMention; +import dev.sheldan.abstracto.core.repository.AllowedMentionRepository; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Optional; + +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class AllowedMentionManagementServiceBeanTest { + + @InjectMocks + private AllowedMentionManagementServiceBean testUnit; + + @Mock + private AllowedMentionRepository allowedMentionRepository; + + @Mock + private ServerManagementService serverManagementService; + + @Mock + private AllowedMention allowedMention; + + @Mock + private AServer server; + + private static final Long SERVER_ID = 4L; + + @Test + public void getCustomAllowedMentionForIdExisting() { + when(allowedMentionRepository.findById(SERVER_ID)).thenReturn(Optional.of(allowedMention)); + Optional foundMentionOptional = testUnit.getCustomAllowedMentionFor(SERVER_ID); + Assert.assertTrue(foundMentionOptional.isPresent()); + foundMentionOptional.ifPresent(foundMention -> Assert.assertEquals(allowedMention, foundMention)); + } + + @Test + public void getCustomAllowedMentionForIdNotExisting() { + when(allowedMentionRepository.findById(SERVER_ID)).thenReturn(Optional.empty()); + Optional foundMentionOptional = testUnit.getCustomAllowedMentionFor(SERVER_ID); + Assert.assertFalse(foundMentionOptional.isPresent()); + } + + @Test + public void testGetCustomAllowedMentionForServer() { + when(allowedMentionRepository.findByServer(server)).thenReturn(Optional.of(allowedMention)); + Optional foundMentionOptional = testUnit.getCustomAllowedMentionFor(server); + Assert.assertTrue(foundMentionOptional.isPresent()); + foundMentionOptional.ifPresent(foundMention -> Assert.assertEquals(allowedMention, foundMention)); + } + + @Test + public void hasCustomAllowedMention() { + when(allowedMentionRepository.findById(SERVER_ID)).thenReturn(Optional.of(allowedMention)); + Assert.assertTrue(testUnit.hasCustomAllowedMention(SERVER_ID)); + } + + @Test + public void createCustomAllowedMention() { + when(serverManagementService.loadOrCreate(SERVER_ID)).thenReturn(server); + AllowedMention createdMention = testUnit.createCustomAllowedMention(SERVER_ID, allowedMention); + ArgumentCaptor mentionCaptor = ArgumentCaptor.forClass(AllowedMention.class); + verify(allowedMentionRepository, times(1)).save(mentionCaptor.capture()); + Assert.assertEquals(createdMention, mentionCaptor.getValue()); + Assert.assertEquals(server, createdMention.getServer()); + } + + @Test + public void deleteCustomAllowedMention() { + testUnit.deleteCustomAllowedMention(SERVER_ID); + verify(allowedMentionRepository, times(1)).deleteById(SERVER_ID); + } +} \ No newline at end of file diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/exception/UnknownMentionTypeException.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/exception/UnknownMentionTypeException.java new file mode 100644 index 000000000..d12c4fdc6 --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/exception/UnknownMentionTypeException.java @@ -0,0 +1,15 @@ +package dev.sheldan.abstracto.core.exception; + +import dev.sheldan.abstracto.templating.Templatable; + +public class UnknownMentionTypeException extends AbstractoRunTimeException implements Templatable { + @Override + public String getTemplateName() { + return "unknown_mention_type_exception"; + } + + @Override + public Object getTemplateModel() { + return new Object(); + } +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java index b5ab53652..ca3d30e43 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java @@ -44,9 +44,14 @@ public class AServer implements SnowFlake, Serializable { this.updated = Instant.now(); } + @OneToOne(mappedBy = "server", cascade = CascadeType.ALL) + @PrimaryKeyJoinColumn + private AllowedMention allowedMention; + @OneToMany( fetch = FetchType.LAZY, orphanRemoval = true, + cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "server") @Builder.Default @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AllowedMention.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AllowedMention.java new file mode 100644 index 000000000..2a0f4f981 --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AllowedMention.java @@ -0,0 +1,64 @@ +package dev.sheldan.abstracto.core.models.database; + +import lombok.*; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +import javax.persistence.*; +import java.io.Serializable; +import java.time.Instant; + +@Entity +@Table(name="allowed_mention") +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode +@Cacheable +@Getter +@Setter +@ToString +@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +public class AllowedMention implements Serializable { + + @Id + @Column(name = "server_id") + private Long id; + + @OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @MapsId + @JoinColumn(name = "server_id") + private AServer server; + + @Getter + @Column(name = "everyone_mention") + private Boolean everyone; + + @Getter + @Column(name = "user_mention") + private Boolean user; + + @Getter + @Column(name = "role_mention") + private Boolean role; + + @Column(name = "created") + private Instant created; + + @PrePersist + private void onInsert() { + this.created = Instant.now(); + } + + @Column(name = "updated") + private Instant updateTimestamp; + + @PreUpdate + private void onUpdate() { + this.updateTimestamp = Instant.now(); + } + + public boolean allAllowed() { + return everyone && user && role; + } + +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/AllowedMentionService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/AllowedMentionService.java new file mode 100644 index 000000000..f755976da --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/AllowedMentionService.java @@ -0,0 +1,16 @@ +package dev.sheldan.abstracto.core.service; + +import dev.sheldan.abstracto.core.models.database.AllowedMention; +import net.dv8tion.jda.api.entities.Message; + +import java.util.List; + +public interface AllowedMentionService { + boolean allMentionsAllowed(Long serverId); + List getAllowedMentionTypesForServer(Long serverId); + void allowMentionForServer(Message.MentionType mentionType, Long serverId); + void disAllowMentionForServer(Message.MentionType mentionType, Long serverId); + AllowedMention getDefaultAllowedMention(); + AllowedMention getEffectiveAllowedMention(Long serverId); + Message.MentionType getMentionTypeFromString(String input); +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/management/AllowedMentionManagementService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/management/AllowedMentionManagementService.java new file mode 100644 index 000000000..32043fe57 --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/management/AllowedMentionManagementService.java @@ -0,0 +1,14 @@ +package dev.sheldan.abstracto.core.service.management; + +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.models.database.AllowedMention; + +import java.util.Optional; + +public interface AllowedMentionManagementService { + Optional getCustomAllowedMentionFor(Long serverId); + Optional getCustomAllowedMentionFor(AServer server); + boolean hasCustomAllowedMention(Long serverId); + AllowedMention createCustomAllowedMention(Long server, AllowedMention base); + void deleteCustomAllowedMention(Long serverId); +} diff --git a/abstracto-application/documentation/src/main/docs/asciidoc/modules/core.adoc b/abstracto-application/documentation/src/main/docs/asciidoc/modules/core.adoc index afb65f3f3..d1784a6e0 100644 --- a/abstracto-application/documentation/src/main/docs/asciidoc/modules/core.adoc +++ b/abstracto-application/documentation/src/main/docs/asciidoc/modules/core.adoc @@ -114,6 +114,14 @@ Listing all feature modes:: Setting up a feature with an interactive wizard:: Usage: `setupFeature ` * Description: Starts an interactive wizard to configure the necessary properties and post targets of a feature. Also includes custom steps. Closes with a summary page to see all changes. +Allow the bot to use certain mentions:: +Usage: `allowMention ` +* Description: Allows the bot to use certain mentions. ´mentionType` can either be `everyone`, `role` or `user`. If @everyone is enabled, this also enabled @here mentions. +This change takes immediate effect and is only for the current server. Per default user and role mentions are enabled. +Disallow the bot to use certain mentions:: +Usage: `disallowMention ` +* Description: Disallows the bot to use certain mentions. ´mentionType` can either be `everyone`, `role` or `user`. If @everyone is disabled, this also disables @here mentions. +This change takes immediate effect and is only for the current server. Per default everyone/here mentions are disabled. .What does it mean if a role is immune?