Compare commits

...

24 Commits

Author SHA1 Message Date
Sheldan
b16cef0d3c [maven-release-plugin] prepare release abstracto-application-1.3.7 2021-08-09 23:52:40 +02:00
Sheldan
cc55934ff2 [AB-xxx] fixing not removing component payloads for message embed cleanup job 2021-08-09 00:40:36 +02:00
Sheldan
168b4a52c8 [AB-xxx] changing some exception logging
fixing moderator member not re-used for reply command
2021-08-09 00:11:49 +02:00
Sheldan
73b5684a7e [maven-release-plugin] prepare for next development iteration 2021-07-26 01:37:38 +02:00
Sheldan
23ba9a88aa [maven-release-plugin] prepare release abstracto-application-1.3.6 2021-07-26 01:37:31 +02:00
Sheldan
a5664946e1 [AB-xxx] hotfix for component payload 2021-07-26 01:30:47 +02:00
Sheldan
ec16548cea [maven-release-plugin] prepare for next development iteration 2021-07-25 20:28:46 +02:00
Sheldan
9c9082034a [maven-release-plugin] prepare release abstracto-application-1.3.5 2021-07-25 20:28:37 +02:00
Sheldan
1486a0e9c3 [AB-314] forcing modmail messages being logged in certain order 2021-07-25 20:17:32 +02:00
Sheldan
533f1afcd5 [AB-313] removing restriction to string parameter for combined parameters 2021-07-25 17:28:42 +02:00
Sheldan
a6c3bb5aa2 [AB-313] ignoring referenced message parameter in parameter handler if not needed 2021-07-25 17:27:32 +02:00
Sheldan
5311cfcc2e [AB-xxx] fixing not showing assignable role actions in metrics 2021-07-25 15:18:54 +02:00
Sheldan
ee7f9180dc [AB-xxx] adding JDA metrics for all events
adding support to template environment variables in liquibase configuration
removing not needed column from component payload
2021-07-25 15:17:27 +02:00
Sheldan
3f67593ef4 [maven-release-plugin] prepare for next development iteration 2021-07-21 23:34:33 +02:00
Sheldan
0ccee3b211 [maven-release-plugin] prepare release abstracto-application-1.3.4 2021-07-21 23:34:25 +02:00
Sheldan
86b9ccb164 [AB-300] adding ability to determine the channel names for modmail 2021-07-21 01:38:04 +02:00
Sheldan
bc1eb0b55f [AB-305] fixing not correctly persisting removal of an assignable role from an assignable role place 2021-07-21 00:46:11 +02:00
Sheldan
92a8b5ba64 [AB-311] adding softban command 2021-07-20 02:02:42 +02:00
Sheldan
7535b2e66d [AB-xxx] make ban reason mandatory 2021-07-18 19:29:58 +02:00
Sheldan
7117ac26d3 [AB-308] adding a separate type for assignable role places to enable booster only places
adding more detailed logging to assignable roles
adding some fall through logic to the banned listener to always log at least the basic information
refactoring some command structure for showing configuration, so the command actually executes the message response
fixing potential exception case for starboard updates causing the message ID to not be persisted
2021-07-18 19:20:14 +02:00
Sheldan
32056cd6b9 [maven-release-plugin] prepare for next development iteration 2021-07-14 02:17:57 +02:00
Sheldan
efd6c23713 [maven-release-plugin] prepare release abstracto-application-1.3.3 2021-07-14 02:17:48 +02:00
Sheldan
794fc7ceac [AB-71] adding very simple anti raid feature to automatically mute member who mass mention users 2021-07-14 01:48:53 +02:00
Sheldan
6b5a255aa8 [maven-release-plugin] prepare for next development iteration 2021-07-13 01:19:43 +02:00
179 changed files with 2907 additions and 263 deletions

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>anti-raid-impl</artifactId>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>anti-raid-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/liquibase.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,18 @@
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>liquibase</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<outputDirectory>.</outputDirectory>
<directory>${project.basedir}/src/main/resources/migrations</directory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.antiraid.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:antiRaid-config.properties")
public class AntiRaidProperties {
}

View File

@@ -0,0 +1,36 @@
package dev.sheldan.abstracto.antiraid.listener;
import dev.sheldan.abstracto.antiraid.config.AntiRaidFeatureDefinition;
import dev.sheldan.abstracto.antiraid.service.MassPingService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.ChannelType;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class MassPingMessageListener implements AsyncMessageReceivedListener {
@Autowired
private MassPingService massPingService;
@Override
public DefaultListenerResult execute(MessageReceivedModel model) {
Message message = model.getMessage();
if(message.getAuthor().isBot() || message.isWebhookMessage() || !message.isFromGuild() || !message.isFromType(ChannelType.TEXT)) {
return DefaultListenerResult.IGNORED;
}
massPingService.processMessage(message);
return DefaultListenerResult.PROCESSED;
}
@Override
public FeatureDefinition getFeature() {
return AntiRaidFeatureDefinition.ANTI_RAID;
}
}

View File

@@ -0,0 +1,109 @@
package dev.sheldan.abstracto.antiraid.service;
import dev.sheldan.abstracto.antiraid.config.AntiRaidPostTarget;
import dev.sheldan.abstracto.antiraid.model.MassPingNotificationModel;
import dev.sheldan.abstracto.core.models.ConditionContextInstance;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.ConditionService;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.service.MuteService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class MassPingServiceBean implements MassPingService {
private static final String MASS_PING_MUTE_NOTIFICATION_TEMPLATE_KEY = "massPing_mute_notification";
private static final String LEVEL_CONDITION_NAME = "HAS_LEVEL";
private static final String LEVEL_CONDITION_USER_ID_PARAMETER = "userId";
private static final String LEVEL_CONDITION_LEVEL_PARAMETER = "level";
@Autowired
private ConfigService configService;
@Autowired
private MuteService muteService;
@Autowired
private TemplateService templateService;
@Autowired
private MassPingServiceBean self;
@Autowired
private PostTargetService postTargetService;
@Autowired
private ConditionService conditionService;
@Value("${abstracto.massPing.maxAllowedMentions}")
private Integer maxAllowedMentions;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Override
public CompletableFuture<Void> processMessage(Message message) {
if(message.getMentionedMembers().size() > maxAllowedMentions) {
Integer level = configService.getLongValueOrConfigDefault(MassPingService.MAX_AFFECTED_LEVEL_KEY, message.getGuild().getIdLong()).intValue();
boolean allowed = allowedToMassMention(message, level);
if(!allowed) {
return muteService.muteMemberWithoutContext(message.getMember())
.thenAccept(unused -> self.sendMassPingMuteNotification(message))
.thenAccept(unused -> log.info("Muted member {} in server {} because of too many member mentions. (> {}).",
message.getMember().getIdLong(), message.getGuild().getIdLong(), maxAllowedMentions));
} else {
log.info("User {} in server {} is allowed to mass mention, because of level (or lack of level configuration).",
message.getMember().getIdLong(), message.getGuild().getIdLong());
return CompletableFuture.completedFuture(null);
}
} else {
return CompletableFuture.completedFuture(null);
}
}
private boolean allowedToMassMention(Message message, Integer level) {
log.info("Checking if member {} is allowed to mention a lot of users in server {}.", message.getAuthor().getIdLong(), message.getGuild().getIdLong());
Map<String, Object> parameters = new HashMap<>();
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(message.getMember());
parameters.put(LEVEL_CONDITION_USER_ID_PARAMETER, userInAServer.getUserInServerId());
parameters.put(LEVEL_CONDITION_LEVEL_PARAMETER, level);
ConditionContextInstance contextInstance = ConditionContextInstance
.builder()
.conditionName(LEVEL_CONDITION_NAME)
.parameters(parameters)
.build();
return conditionService.checkConditions(contextInstance);
}
@Transactional
public CompletableFuture<Void> sendMassPingMuteNotification(Message message) {
Member member = message.getMember();
MassPingNotificationModel model = MassPingNotificationModel
.builder()
.messageLink(message.getJumpUrl())
.mentionCount(message.getMentionedMembers().size())
.messageContent(message.getContentRaw())
.memberDisplay(MemberDisplay.fromMember(member))
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(MASS_PING_MUTE_NOTIFICATION_TEMPLATE_KEY, model, member.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, AntiRaidPostTarget.MASS_PING_LOG, member.getGuild().getIdLong()));
}
}

View File

@@ -0,0 +1,9 @@
abstracto.featureFlags.antiRaid.featureName=antiRaid
abstracto.featureFlags.antiRaid.enabled=false
abstracto.postTargets.massPingLog.name=massPingLog
abstracto.massPing.maxAllowedMentions=5
abstracto.systemConfigs.massPingMinLevel.name=massPingMinLevel
abstracto.systemConfigs.massPingMinLevel.longValue=15

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>anti-raid-int</artifactId>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,39 @@
package dev.sheldan.abstracto.antiraid.config;
import dev.sheldan.abstracto.antiraid.service.MassPingService;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.PostTargetEnum;
import dev.sheldan.abstracto.moderation.config.feature.MutingFeatureConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class AntiRaidFeatureConfig implements FeatureConfig {
@Autowired
private MutingFeatureConfig mutingFeatureConfig;
@Override
public FeatureDefinition getFeature() {
return AntiRaidFeatureDefinition.ANTI_RAID;
}
@Override
public List<PostTargetEnum> getRequiredPostTargets() {
return Arrays.asList(AntiRaidPostTarget.MASS_PING_LOG);
}
@Override
public List<String> getRequiredSystemConfigKeys() {
return Arrays.asList(MassPingService.MAX_AFFECTED_LEVEL_KEY);
}
@Override
public List<FeatureConfig> getRequiredFeatures() {
return Arrays.asList(mutingFeatureConfig);
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.antiraid.config;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import lombok.Getter;
@Getter
public enum AntiRaidFeatureDefinition implements FeatureDefinition {
ANTI_RAID("antiRaid");
private String key;
AntiRaidFeatureDefinition(String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.antiraid.config;
import dev.sheldan.abstracto.core.config.PostTargetEnum;
import lombok.Getter;
@Getter
public enum AntiRaidPostTarget implements PostTargetEnum {
MASS_PING_LOG("massPingLog");
private String key;
AntiRaidPostTarget(String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,16 @@
package dev.sheldan.abstracto.antiraid.model;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class MassPingNotificationModel {
private MemberDisplay memberDisplay;
private String messageContent;
private String messageLink;
private Integer mentionCount;
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.antiraid.service;
import net.dv8tion.jda.api.entities.Message;
import java.util.concurrent.CompletableFuture;
public interface MassPingService {
String MAX_AFFECTED_LEVEL_KEY = "massPingMinLevel";
CompletableFuture<Void> processMessage(Message message);
}

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>anti-raid</artifactId>
<packaging>pom</packaging>
<modules>
<module>anti-raid-int</module>
<module>anti-raid-impl</module>
</modules>
</project>

View File

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

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
@@ -41,11 +42,15 @@ public class CreateAssignableRolePost extends AbstractConditionableCommand {
String name = (String) parameters.get(0);
TextChannel channel = (TextChannel) parameters.get(1);
String text = (String) parameters.get(2);
AssignableRolePlaceType type = AssignableRolePlaceType.DEFAULT;
if(parameters.size() > 3) {
type = (AssignableRolePlaceType) parameters.get(3);
}
if(!channel.getGuild().equals(commandContext.getGuild())) {
throw new EntityGuildMismatchException();
}
AChannel chosenChannel = channelManagementService.loadChannel(channel.getIdLong());
service.createAssignableRolePlace(name, chosenChannel, text);
service.createAssignableRolePlace(name, chosenChannel, text, type);
return CommandResult.fromSuccess();
}
@@ -54,10 +59,11 @@ public class CreateAssignableRolePost extends AbstractConditionableCommand {
List<ParameterValidator> rolePlaceNameValidator = Arrays.asList(MaxStringLengthValidator.max(AssignableRolePlace.ASSIGNABLE_PLACE_NAME_LIMIT));
Parameter rolePostName = Parameter.builder().name("name").validators(rolePlaceNameValidator).type(String.class).templated(true).build();
Parameter channel = Parameter.builder().name("channel").type(TextChannel.class).templated(true).build();
Parameter type = Parameter.builder().name("type").type(AssignableRolePlaceType.class).templated(true).optional(true).build();
List<ParameterValidator> rolePlaceDescriptionValidator = Arrays.asList(MaxStringLengthValidator.max(AssignableRolePlace.ASSIGNABLE_PLACE_NAME_LIMIT));
Parameter text = Parameter.builder().name("text").validators(rolePlaceDescriptionValidator).type(String.class).remainder(true).optional(true).templated(true).build();
Parameter text = Parameter.builder().name("text").validators(rolePlaceDescriptionValidator).type(String.class).templated(true).build();
List<String> aliases = Arrays.asList("crRPl", "crAssRoPl");
List<Parameter> parameters = Arrays.asList(rolePostName, channel, text);
List<Parameter> parameters = Arrays.asList(rolePostName, channel, text, type);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("createAssignableRolePlace")

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.model.template.AssignableRolePlaceConfig;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
@@ -9,8 +10,9 @@ import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -24,21 +26,24 @@ import java.util.concurrent.CompletableFuture;
@Component
public class ShowAssignableRolePlaceConfig extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ChannelService channelService;
public static final String ASSIGNABLE_ROLES_CONFIG_POST_TEMPLATE_KEY = "assignable_roles_config_post";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
AServer server = serverManagementService.loadServer(commandContext.getGuild());
// TODO refactor to return something to be posted in this command here instead of relying it to be posted somewhere else
return service.showAssignablePlaceConfig(server, name, commandContext.getChannel())
.thenApply(unused -> CommandResult.fromIgnored());
AssignableRolePlaceConfig config = service.getAssignableRolePlaceConfig(commandContext.getGuild(), name);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(ASSIGNABLE_ROLES_CONFIG_POST_TEMPLATE_KEY, config, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
@@ -51,7 +56,7 @@ public class ShowAssignableRolePlaceConfig extends AbstractConditionableCommand
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.async(true)
.causesReaction(true)
.causesReaction(false)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.model.template.AssignablePlaceOverview;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
@@ -9,7 +10,9 @@ import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -28,11 +31,16 @@ public class ShowAssignableRolePlaces extends AbstractConditionableCommand {
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ChannelService channelService;
public static final String ASSIGNABLE_ROLE_PLACES_OVERVIEW_TEMPLATE_KEY = "assignable_role_places_overview";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
AServer server = serverManagementService.loadServer(commandContext.getGuild());
return service.showAllAssignableRolePlaces(server, commandContext.getChannel())
.thenApply(aVoid -> CommandResult.fromIgnored());
AssignablePlaceOverview model = service.getAssignableRolePlaceOverview(commandContext.getGuild());
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(ASSIGNABLE_ROLE_PLACES_OVERVIEW_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
@@ -42,7 +50,6 @@ public class ShowAssignableRolePlaces extends AbstractConditionableCommand {
.name("showAssignableRolePlaces")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.async(true)
.supportsEmbedException(true)
.help(helpInfo)

View File

@@ -2,11 +2,13 @@ package dev.sheldan.abstracto.assignableroles.listener;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRoleNotFoundException;
import dev.sheldan.abstracto.assignableroles.exception.BoosterAssignableRolePlaceMemberNotBoostingException;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionResult;
import dev.sheldan.abstracto.assignableroles.model.AssignableRolePlacePayload;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRolePlaceConditionModel;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import dev.sheldan.abstracto.assignableroles.model.template.AssignableRoleSuccessNotificationModel;
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleConditionServiceBean;
@@ -73,9 +75,10 @@ public class AssignableRoleButtonClickedListener implements ButtonClickedListene
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
ButtonClickEvent event = model.getEvent();
AssignableRolePlacePayload payload = (AssignableRolePlacePayload) model.getDeserializedPayload();
AssignableRolePlace place = assignableRolePlaceManagementService.findByPlaceId(payload.getPlaceId());
if(event.getGuild() != null && event.getMember() != null) {
Member member = event.getMember();
if(event.getGuild() != null && member != null) {
AssignableRolePlacePayload payload = (AssignableRolePlacePayload) model.getDeserializedPayload();
AssignableRolePlace place = assignableRolePlaceManagementService.findByPlaceId(payload.getPlaceId());
Guild guild = event.getGuild();
List<Role> removedRoles = new ArrayList<>();
Role roleById = guild.getRoleById(payload.getRoleId());
@@ -88,19 +91,25 @@ public class AssignableRoleButtonClickedListener implements ButtonClickedListene
throw new AssignableRoleNotFoundException(payload.getRoleId());
}
if(roleById != null) {
boolean memberHasRole = event
.getMember()
boolean memberHasRole = member
.getRoles()
.stream()
.anyMatch(memberRole -> memberRole.getIdLong() == payload.getRoleId());
if(!memberHasRole) {
if(place.getType().equals(AssignableRolePlaceType.BOOSTER) && member.getTimeBoosted() == null) {
assignableRoleService.assignableRoleConditionFailure();
throw new BoosterAssignableRolePlaceMemberNotBoostingException();
}
AssignableRole assignableRole = assignableRoleOptional.get();
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(event.getMember());
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
if(!assignableRole.getConditions().isEmpty()) {
log.debug("Evaluating {} conditions for assignable role {}.", assignableRole.getConditions().size(), assignableRole.getId());
AssignableRoleConditionResult conditionResult =
assignableRoleConditionServiceBean.evaluateConditions(assignableRole.getConditions(), aUserInAServer, roleById);
if(!conditionResult.getFulfilled()) {
log.info("One condition failed to be fulfilled - notifying user.");
self.notifyUserAboutConditionFail(model, event.getInteraction(), conditionResult.getModel());
assignableRoleService.assignableRoleConditionFailure();
return ButtonClickedListenerResult.ACKNOWLEDGED;
}
}
@@ -116,9 +125,10 @@ public class AssignableRoleButtonClickedListener implements ButtonClickedListene
.map(roleOfUser -> guild.getRoleById(roleOfUser.getRole().getId()))
.filter(Objects::nonNull)
.collect(Collectors.toList());
log.info("Removing {} because of unique role configuration in place {}.", rolesToRemove.size(), place.getId());
removedRoles.addAll(rolesToRemove);
List<CompletableFuture<Void>> removalFutures = new ArrayList<>();
rolesToRemove.forEach(roleToRemove -> removalFutures.add(roleService.removeRoleFromUserAsync(event.getMember(), roleToRemove)));
rolesToRemove.forEach(roleToRemove -> removalFutures.add(roleService.removeRoleFromUserAsync(member, roleToRemove)));
removalFuture = new CompletableFutureList<>(removalFutures).getMainFuture();
} else {
removalFuture = CompletableFuture.completedFuture(null);
@@ -126,29 +136,29 @@ public class AssignableRoleButtonClickedListener implements ButtonClickedListene
} else {
removalFuture = CompletableFuture.completedFuture(null);
}
CompletableFuture<Void> roleAdditionFuture = roleService.addRoleToMemberAsync(event.getMember(), roleById);
CompletableFuture<Void> roleAdditionFuture = assignableRoleService.assignAssignableRoleToUser(roleById, member);
CompletableFuture.allOf(removalFuture, roleAdditionFuture).whenComplete((unused, throwable) -> {
if(throwable != null) {
log.error("Failed to either add or remove roles for assignable role place {} in server {}.", payload.getPlaceId(), guild.getIdLong());
}
if(!roleAdditionFuture.isCompletedExceptionally()) {
log.info("Added role {} to member {} in server {} for assignable role interaction {} on component {}.",
roleById.getId(), event.getMember().getId(), guild.getIdLong(), event.getInteraction().getId(), event.getComponentId());
roleById.getId(), member.getId(), guild.getIdLong(), event.getInteraction().getId(), event.getComponentId());
self.notifyUser(model, true, roleById, event.getInteraction(), removedRoles).thenAccept(unused1 -> {
log.info("Persisting adding assignable role update for user {} in server {} of role {}.", event.getMember().getIdLong(), guild.getIdLong(), roleById.getId());
self.persistAssignableUser(event.getMember(), payload, false);
log.info("Persisting adding assignable role update for user {} in server {} of role {}.", member.getIdLong(), guild.getIdLong(), roleById.getId());
self.persistAssignableUser(member, payload, false);
});
}
});
} else {
roleService.removeRoleFromUserAsync(event.getMember(), roleById)
assignableRoleService.removeAssignableRoleFromUser(roleById, member)
.thenAccept(unused -> {
self.notifyUser(model, false, roleById, event.getInteraction(), new ArrayList<>());
log.info("Removed role {} from member {} in server {} for assignable role interaction {} on component {}.",
roleById.getId(), event.getMember().getId(), guild.getIdLong(), event.getInteraction().getId(), event.getComponentId());
roleById.getId(), member.getId(), guild.getIdLong(), event.getInteraction().getId(), event.getComponentId());
}).thenAccept(unused -> {
log.info("Persisting remove assignable role update for user {} in server {} of role {}.", event.getMember().getIdLong(), guild.getIdLong(), roleById.getId());
self.persistAssignableUser(event.getMember(), payload, true);
log.info("Persisting remove assignable role update for user {} in server {} of role {}.", member.getIdLong(), guild.getIdLong(), roleById.getId());
self.persistAssignableUser(member, payload, true);
});
}
} else {
@@ -183,6 +193,8 @@ public class AssignableRoleButtonClickedListener implements ButtonClickedListene
@Transactional
public CompletableFuture<Void> notifyUser(ButtonClickedListenerModel model, boolean roleAdded, Role role, ButtonInteraction buttonInteraction, List<Role> removedRoles) {
log.info("Notifying user {} in server {} in channel {} about role change with role {}.",
buttonInteraction.getUser().getIdLong(), buttonInteraction.getGuild().getIdLong(), buttonInteraction.getChannel().getIdLong(), role.getId());
AssignableRoleSuccessNotificationModel notificationModel = AssignableRoleSuccessNotificationModel
.builder()
.added(roleAdded)
@@ -196,6 +208,8 @@ public class AssignableRoleButtonClickedListener implements ButtonClickedListene
@Transactional
public CompletableFuture<Void> notifyUserAboutConditionFail(ButtonClickedListenerModel model, ButtonInteraction buttonInteraction,
AssignableRolePlaceConditionModel conditionModel) {
log.info("Notifying user {} in server {} in channel {} about failed condition.", buttonInteraction.getUser().getIdLong(),
buttonInteraction.getGuild().getIdLong(), buttonInteraction.getChannel().getIdLong());
return FutureUtils.toSingleFutureGeneric(
interactionService.sendMessageToInteraction("assignable_role_condition_notification", conditionModel, buttonInteraction.getHook())) ;
}

View File

@@ -0,0 +1,112 @@
package dev.sheldan.abstracto.assignableroles.listener;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRoleManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMemberBoostTimeUpdateListener;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.listener.BoostTimeUpdatedModel;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
@Slf4j
public class AssignableRolePlaceBoostTimeUpdateListener implements AsyncMemberBoostTimeUpdateListener {
@Autowired
private AssignedRoleUserManagementService assignedRoleUserManagementService;
@Autowired
private AssignableRoleManagementService assignableRoleManagementService;
@Autowired
private RoleService roleService;
@Autowired
private AssignableRoleService assignableRoleService;
@Autowired
private AssignableRolePlaceBoostTimeUpdateListener self;
@Override
public DefaultListenerResult execute(BoostTimeUpdatedModel model) {
Member member = model.getMember();
if(member.getTimeBoosted() == null) {
removeAssignedBoosterRoles(member);
return DefaultListenerResult.PROCESSED;
}
return DefaultListenerResult.IGNORED;
}
private void removeAssignedBoosterRoles(Member member) {
log.info("Member {} in server {} stopped boosting.", member.getIdLong(), member.getGuild().getIdLong());
ServerUser serverUser = ServerUser.fromMember(member);
Optional<AssignedRoleUser> assignedRoleUserOptional = assignedRoleUserManagementService.findByUserInServerOptional(serverUser);
if(assignedRoleUserOptional.isPresent()) {
AssignedRoleUser assignedRoleUser = assignedRoleUserOptional.get();
List<AssignableRole> boosterRoles = assignableRoleManagementService.getAssignableRolesFromAssignableUserWithPlaceType(assignedRoleUser, AssignableRolePlaceType.BOOSTER);
if(!boosterRoles.isEmpty()) {
log.info("Removing {} assignable role mappings.", boosterRoles.size());
Guild guild = member.getGuild();
List<Role> actualRolesToDelete = boosterRoles
.stream()
.map(assignableRole -> guild.getRoleById(assignableRole.getRole().getId()))
.filter(Objects::nonNull)
.collect(Collectors.toList());
log.debug("Which translated to {} roles in reality.", actualRolesToDelete.size());
List<CompletableFuture<Void>> list = new ArrayList<>();
actualRolesToDelete.forEach(role -> list.add(roleService.removeRoleFromUserAsync(member, role)));
FutureUtils.toSingleFutureGeneric(list)
.thenAccept(unused -> self.clearPersistedBoosterAssignableRoles(member))
.exceptionally(throwable -> {
log.warn("One or more roles might have failed to remove. ", throwable);
self.clearPersistedBoosterAssignableRoles(member);
return null;
});
} else {
log.info("Member {} in server {} did not have boost roles - doing nothing.", member.getIdLong(), member.getGuild().getIdLong());
}
} else {
log.info("Member (ID {}) in server (ID: {}), who was not tracked via assignable roles, stopped boosting - doing nothing.",
member.getIdLong(), member.getGuild().getIdLong());
}
}
@Transactional
public void clearPersistedBoosterAssignableRoles(Member member) {
ServerUser serverUser = ServerUser.fromMember(member);
Optional<AssignedRoleUser> assignedRoleUserOptional = assignedRoleUserManagementService.findByUserInServerOptional(serverUser);
if(assignedRoleUserOptional.isPresent()) {
AssignedRoleUser assignedRoleUser = assignedRoleUserOptional.get();
List<AssignableRole> boosterRoles = assignableRoleManagementService.getAssignableRolesFromAssignableUserWithPlaceType(assignedRoleUser, AssignableRolePlaceType.BOOSTER);
assignableRoleService.removeAssignableRolesFromAssignableRoleUser(boosterRoles, assignedRoleUser);
} else {
log.warn("No assigned role user found for member {} in server {}.", member.getIdLong(), member.getGuild().getIdLong());
}
}
@Override
public FeatureDefinition getFeature() {
return AssignableRoleFeatureDefinition.ASSIGNABLE_ROLES;
}
}

View File

@@ -1,12 +1,17 @@
package dev.sheldan.abstracto.assignableroles.repository;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* Repository to manage the access to the table managed by {@link AssignableRole assignableRole}
*/
@Repository
public interface AssignableRoleRepository extends JpaRepository<AssignableRole, Long> {
List<AssignableRole> findByAssignedUsersContainingAndAssignablePlace_Type(AssignedRoleUser roleUser, AssignableRolePlaceType type);
}

View File

@@ -16,6 +16,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,6 +28,7 @@ import java.util.Optional;
import java.util.stream.Collectors;
@Component
@Slf4j
public class AssignableRoleConditionServiceBean implements AssignableRoleConditionService {
@Autowired
@@ -55,12 +57,15 @@ public class AssignableRoleConditionServiceBean implements AssignableRoleConditi
@Override
public AssignableRoleConditionResult evaluateConditions(List<AssignableRoleCondition> conditions, AUserInAServer aUserInAServer, Role role) {
log.debug("Evaluating {} conditions for role {}.", conditions.size(), role.getId());
for (AssignableRoleCondition condition : conditions) {
if(assignableRoleConditionEvaluators != null) {
Optional<AssignableRoleConditionEvaluator> evaluatorOptional = findEvaluatorForCondition(condition.getType());
if(evaluatorOptional.isPresent()) {
AssignableRoleConditionEvaluator evaluator = evaluatorOptional.get();
log.debug("Evaluating condition {} with evaluator {}.", condition.getType(), evaluator.getClass());
if(!evaluator.fulfillsCondition(condition, aUserInAServer)) {
log.info("Condition {} failed for role {} in server {}.", condition.getType(), role.getId(), aUserInAServer.getServerReference().getId());
return AssignableRoleConditionResult.fromFail(condition.getType(), evaluator.createNotificationModel(condition, role));
}
}
@@ -94,6 +99,7 @@ public class AssignableRoleConditionServiceBean implements AssignableRoleConditi
if(assignableRoleConditionManagementService.findAssignableRoleCondition(assignableRole, type).isPresent()) {
throw new AssignableRoleConditionAlreadyExistsException();
}
log.info("Creating new condition for role {} in place {} in server {}.", place.getId(), role.getId(), role.getGuild().getIdLong());
return assignableRoleConditionManagementService.createAssignableRoleCondition(assignableRole, type, value);
}
@@ -106,6 +112,7 @@ public class AssignableRoleConditionServiceBean implements AssignableRoleConditi
if(!existingCondition.isPresent()) {
throw new AssignableRoleConditionDoesNotExistException();
}
log.info("Deleting assignable role condition on place {} for role {} in server {}.", place.getId(), role.getId(), role.getGuild().getIdLong());
existingCondition.ifPresent(condition -> assignableRoleConditionManagementService.deleteAssignableRoleCondition(condition));
}

View File

@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.assignableroles.exception.*;
import dev.sheldan.abstracto.assignableroles.model.AssignableRolePlacePayload;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.model.template.*;
import dev.sheldan.abstracto.assignableroles.service.management.*;
import dev.sheldan.abstracto.core.command.exception.CommandParameterKeyValueWrongTypeException;
@@ -21,7 +22,6 @@ import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.*;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.interactions.components.ButtonStyle;
@@ -38,9 +38,7 @@ import java.util.stream.Collectors;
@Slf4j
public class AssignableRolePlaceServiceBean implements AssignableRolePlaceService {
public static final String ASSIGNABLE_ROLES_CONFIG_POST_TEMPLATE_KEY = "assignable_roles_config_post";
public static final String ASSIGNABLE_ROLES_POST_TEMPLATE_KEY = "assignable_roles_post";
public static final String ASSIGNABLE_ROLE_PLACES_OVERVIEW_TEMPLATE_KEY = "assignable_role_places_overview";
public static final int MAX_ASSIGNABLE_ROLES_PER_POST = ComponentService.MAX_BUTTONS_PER_ROW * 5;
public static final String ASSIGNABLE_ROLE_COMPONENT_ORIGIN = "assignableRoleButton";
@Autowired
@@ -85,12 +83,18 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
@Autowired
private AssignableRoleConditionService assignableRoleConditionService;
@Autowired
private AssignedRoleUserManagementServiceBean assignedRoleUserManagementServiceBean;
@Autowired
private ServerManagementService serverManagementService;
@Override
public void createAssignableRolePlace(String name, AChannel channel, String text) {
public void createAssignableRolePlace(String name, AChannel channel, String text, AssignableRolePlaceType type) {
if (rolePlaceManagementService.doesPlaceExist(channel.getServer(), name)) {
throw new AssignableRolePlaceAlreadyExistsException(name);
}
rolePlaceManagementService.createPlace(name, channel, text);
rolePlaceManagementService.createPlace(name, channel, text, type);
}
@Override
@@ -98,6 +102,7 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
public CompletableFuture<Void> addRoleToAssignableRolePlace(AServer server, String placeName, Role role, FullEmote fakeEmote, String description) {
AssignableRolePlace assignableRolePlace = rolePlaceManagementService.findByServerAndKey(server, placeName);
if (assignableRolePlace.getAssignableRoles().size() > MAX_ASSIGNABLE_ROLES_PER_POST) {
log.info("Assignable role place {} has already {} roles. Not possible to add more.", assignableRolePlace.getId(), assignableRolePlace.getAssignableRoles().size());
throw new AssignableRolePlaceMaximumRolesException();
}
if (assignableRolePlace.getAssignableRoles().stream().anyMatch(assignableRole -> assignableRole.getRole().getId().equals(role.getIdLong()))) {
@@ -113,16 +118,17 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
throw new EmoteNotUsableException(fakeEmote.getEmote());
}
}
log.debug("There are already message posts on for the assignable role place {}.", assignableRolePlace.getId());
Optional<TextChannel> channelOptional = channelService.getTextChannelFromServerOptional(server.getId(), assignableRolePlace.getChannel().getId());
if (channelOptional.isPresent()) {
TextChannel textChannel = channelOptional.get();
String buttonId = componentService.generateComponentId();
String emoteMarkdown = fakeEmote != null ? fakeEmote.getEmoteRepr() : null;
if (assignableRolePlace.getMessageId() != null) {
return componentService.addButtonToMessage(assignableRolePlace.getMessageId(), textChannel, buttonId, description, emoteMarkdown, ButtonStyle.PRIMARY)
log.debug("Assignable role place {} has already message post with ID {} - updating.", assignableRolePlace.getId(), assignableRolePlace.getMessageId());
return componentService.addButtonToMessage(assignableRolePlace.getMessageId(), textChannel, buttonId, description, emoteMarkdown, ButtonStyle.SECONDARY)
.thenAccept(message -> self.persistAssignableRoleAddition(placeId, role, description, fakeEmote, buttonId));
} else {
log.info("Assignable role place {} is not yet setup - only adding role to the database.", assignableRolePlace.getId());
self.persistAssignableRoleAddition(placeId, role, description, fakeEmote, buttonId);
return CompletableFuture.completedFuture(null);
}
@@ -134,6 +140,7 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
@Transactional
public void persistAssignableRoleAddition(Long placeId, Role role, String description, FullEmote fakeEmote, String componentId) {
AssignableRolePlace place = assignableRolePlaceManagementServiceBean.findByPlaceId(placeId);
log.info("Adding role {} to assignable role place {} with component ID {}.", role.getId(), place.getId(), componentId);
ComponentPayload payload = persistButtonCallback(place, componentId, role.getIdLong());
assignableRoleManagementServiceBean.addRoleToPlace(fakeEmote, role, description, place, payload);
}
@@ -144,6 +151,8 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
Long assignableRolePlaceId = assignableRolePlace.getId();
for (AssignableRole assignableRole : assignableRolePlace.getAssignableRoles()) {
if (assignableRole.getRole().getId().equals(role.getId())) {
log.info("Found {} role to be removed - removing button from place.", role.getId());
// TODO we might want to actually remove all the assigned roles as well
return removeButtonFromAssignableRolePlace(assignableRole, assignableRolePlace).thenAccept(aVoid ->
self.deleteAssignableRoleFromPlace(assignableRolePlaceId, assignableRole.getId())
);
@@ -154,9 +163,12 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
private CompletableFuture<Void> removeButtonFromAssignableRolePlace(AssignableRole assignableRole, AssignableRolePlace assignableRolePlace) {
String componentId = assignableRole.getComponentPayload().getId();
log.debug("Component ID to remove {} for role {}", componentId, assignableRole.getRole().getId());
return channelService.retrieveMessageInChannel(assignableRolePlace.getServer().getId(), assignableRolePlace.getChannel().getId(), assignableRolePlace.getMessageId())
.thenCompose(message ->
componentService.removeComponentWithId(message, componentId, true)
.thenCompose(message -> {
log.debug("Updating message {} to remove component with ID {}.", message.getIdLong(), componentId);
return componentService.removeComponentWithId(message, componentId, true);
}
);
}
@@ -171,6 +183,7 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
.findAny();
roleToRemoveOptional.ifPresent(assignableRole -> {
ComponentPayload componentPayload = assignableRole.getComponentPayload();
assignedRoleUserManagementServiceBean.removeAssignedRoleFromUsers(assignableRole);
assignableRoleManagementServiceBean.deleteAssignableRole(assignableRole);
componentPayloadManagementService.deletePayload(componentPayload);
});
@@ -232,8 +245,10 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
private CompletableFuture<Void> deleteExistingMessagePostsForPlace(AssignableRolePlace assignableRolePlace) {
if (assignableRolePlace.getMessageId() != null) {
log.info("Deleting old message {} for assignable role place {}.", assignableRolePlace.getMessageId(), assignableRolePlace.getId());
return messageService.deleteMessageInChannelInServer(assignableRolePlace.getServer().getId(), assignableRolePlace.getChannel().getId(), assignableRolePlace.getMessageId());
} else {
log.info("Assignable role place {} was not yet set up - no message ID tracked.", assignableRolePlace.getMessageId());
return CompletableFuture.completedFuture(null);
}
}
@@ -299,12 +314,11 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
}
@Override
public CompletableFuture<Void> showAssignablePlaceConfig(AServer server, String name, TextChannel channel) {
Guild guild = guildService.getGuildById(server.getId());
public AssignableRolePlaceConfig getAssignableRolePlaceConfig(Guild guild, String name) {
AServer server = serverManagementService.loadServer(guild);
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
log.info("Showing assignable role place config for place {} in channel {} on server {}.", place.getId(), channel.getId(), server.getId());
AssignableRolePlaceConfig configModel = convertPlaceToAssignableRolePlaceConfig(guild, place);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(ASSIGNABLE_ROLES_CONFIG_POST_TEMPLATE_KEY, configModel, channel));
log.info("Generating assignable role place config for place {} on server {}.", place.getId(), guild.getIdLong());
return convertPlaceToAssignableRolePlaceConfig(guild, place);
}
private AssignableRolePlaceConfig convertPlaceToAssignableRolePlaceConfig(Guild guild, AssignableRolePlace place) {
@@ -326,6 +340,7 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
return AssignableRolePlaceConfig
.builder()
.roles(roles)
.type(place.getType())
.placeName(place.getKey())
.placeText(place.getText())
.uniqueRoles(place.getUniqueRoles())
@@ -336,6 +351,8 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
@Override
public CompletableFuture<Void> moveAssignableRolePlace(AServer server, String name, TextChannel newChannel) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
log.info("Moving assignable role place {} from channel {} to channel {} in guild {}.",
place.getId(), place.getChannel().getId(), newChannel.getId(), newChannel.getGuild().getIdLong());
CompletableFuture<Void> oldPostDeletionFuture = deleteExistingMessagePostsForPlace(place);
Long serverId = server.getId();
Long assignablePlaceId = place.getId();
@@ -363,13 +380,14 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
@Transactional
public void updateAssignableRolePlaceChannel(String name, TextChannel textChannel) {
AChannel channel = channelManagementService.loadChannel(textChannel.getIdLong());
log.info("Setting assignable role place to channel {}.", textChannel.getIdLong());
rolePlaceManagementService.moveAssignableRolePlace(name, channel);
}
@Override
public CompletableFuture<Void> deleteAssignableRolePlace(AServer server, String name) {
AssignableRolePlace place = rolePlaceManagementService.findByServerAndKey(server, name);
log.info("Deleting assignable role place {}.", place.getId());
Long placeId = place.getId();
CompletableFuture<Void> deleteFuture = deleteExistingMessagePostsForPlace(place);
return deleteFuture.thenAccept(unused -> self.deleteAssignableRolePlaceInDatabase(placeId));
@@ -405,25 +423,24 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
}
@Override
public CompletableFuture<Void> showAllAssignableRolePlaces(AServer server, TextChannel channel) {
public AssignablePlaceOverview getAssignableRolePlaceOverview(Guild guild) {
AServer server = serverManagementService.loadServer(guild);
List<AssignableRolePlace> assignableRolePlaces = rolePlaceManagementService.findAllByServer(server);
Guild guild = channel.getGuild();
List<AssignableRolePlaceConfig> placeConfigs = assignableRolePlaces
.stream()
.map(place -> convertPlaceToAssignableRolePlaceConfig(guild, place))
.collect(Collectors.toList());
AssignablePlaceOverview overViewModel = AssignablePlaceOverview
log.info("Showing overview over all assignable role places for server {}.", server.getId());
return AssignablePlaceOverview
.builder()
.places(placeConfigs)
.build();
log.info("Showing overview over all assignable role places for server {} in channel {}.", server.getId(), channel.getId());
List<CompletableFuture<Message>> promises = channelService.sendEmbedTemplateInTextChannelList(ASSIGNABLE_ROLE_PLACES_OVERVIEW_TEMPLATE_KEY, overViewModel, channel);
return CompletableFuture.allOf(promises.toArray(new CompletableFuture[0]));
}
private CompletableFuture<Void> sendAssignablePostMessage(AssignableRolePlace place, TextChannel 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());
CompletableFuture<Message> postFuture = channelService.sendMessageToSendToChannel(messageToSend, channel).get(0);
Long placeId = model.getPlaceId();
return postFuture.thenCompose(unused -> {
@@ -443,6 +460,7 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
public void persistAssignablePlaceMessageId(Long placeId, CompletableFuture<Message> messageFuture) {
AssignableRolePlace place = assignableRolePlaceManagementServiceBean.findByPlaceId(placeId);
Message message = messageFuture.join();
log.info("Setting message ID of assignable role place {} to {}.", placeId, message.getIdLong());
place.setMessageId(message.getIdLong());
}
@@ -491,7 +509,6 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
log.info("Sending assignable role place posts for place {} in channel {} in server {}.", assignableRolePlace.getId(), channel.getId(), serverId);
return sendAssignablePostMessage(assignableRolePlace, channel);
} else {
log.warn("Channel to create assignable role post in does not exist.");
throw new AssignableRolePlaceChannelDoesNotExistException(assignableRolePlace.getChannel().getId(), assignableRolePlace.getKey());
}
}

View File

@@ -24,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@@ -71,14 +72,36 @@ public class AssignableRoleServiceBean implements AssignableRoleService {
.tagList(Arrays.asList(MetricTag.getTag(ACTION, "removed")))
.build();
private static final CounterMetric ASSIGNABLE_ROLES_CONDITION_FAILED =
CounterMetric
.builder()
.name(ASSIGNABLE_ROLES_METRIC)
.tagList(Arrays.asList(MetricTag.getTag(ACTION, "condition")))
.build();
@Override
public CompletableFuture<Void> assignAssignableRoleToUser(Long assignableRoleId, Member member) {
metricService.incrementCounter(ASSIGNABLE_ROLES_ASSIGNED);
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
log.info("Assigning role {} to member {} in server {}.", assignableRoleId, member.getId(), member.getGuild().getId());
metricService.incrementCounter(ASSIGNABLE_ROLES_ASSIGNED);
return roleService.addRoleToMemberAsync(member, role.getRole());
}
@Override
public CompletableFuture<Void> assignAssignableRoleToUser(Role role, Member member) {
return assignRoleToUser(role.getIdLong(), member);
}
@Override
public void assignableRoleConditionFailure() {
metricService.incrementCounter(ASSIGNABLE_ROLES_CONDITION_FAILED);
}
private CompletableFuture<Void> assignRoleToUser(Long roleId, Member member) {
metricService.incrementCounter(ASSIGNABLE_ROLES_ASSIGNED);
return roleService.addRoleToMemberAsync(member, roleId);
}
@Override
public void clearAllRolesOfUserInPlace(AssignableRolePlace place, AUserInAServer userInAServer) {
Optional<AssignedRoleUser> userOptional = assignedRoleUserManagementServiceBean.findByUserInServerOptional(userInAServer);
@@ -102,8 +125,17 @@ public class AssignableRoleServiceBean implements AssignableRoleService {
@Override
public CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, Member member) {
log.info("Removing assignable role {} from user {} in server {}.", assignableRole.getId(), member.getId(), member.getGuild().getId());
return removeRoleFromUser(assignableRole.getRole().getId(), member);
}
@Override
public CompletableFuture<Void> removeAssignableRoleFromUser(Role role, Member member) {
return removeRoleFromUser(role.getIdLong(), member);
}
private CompletableFuture<Void> removeRoleFromUser(Long roleId, Member member) {
metricService.incrementCounter(ASSIGNABLE_ROLES_REMOVED);
return roleService.removeRoleFromMemberAsync(member, assignableRole.getRole());
return roleService.removeRoleFromMemberAsync(member, roleId);
}
@Override
@@ -147,9 +179,17 @@ public class AssignableRoleServiceBean implements AssignableRoleService {
throw new AssignableRoleNotFoundException(roleId);
}
@Override
public void removeAssignableRolesFromAssignableRoleUser(List<AssignableRole> roles, AssignedRoleUser roleUser) {
log.info("Removing {} assignable roles from user {} in server {}.", roles.size(), roleUser.getUser().getUserReference().getId(),
roleUser.getUser().getServerReference().getId());
roles.forEach(assignableRole -> assignedRoleUserManagementServiceBean.removeAssignedRoleFromUser(assignableRole, roleUser));
}
@PostConstruct
public void postConstruct() {
metricService.registerCounter(ASSIGNABLE_ROLES_ASSIGNED, "Assignable roles assigned.");
metricService.registerCounter(ASSIGNABLE_ROLES_REMOVED, "Assignable roles removed.");
metricService.registerCounter(ASSIGNABLE_ROLES_CONDITION_FAILED, "Assignable roles failed to assign because of condition.");
}
}

View File

@@ -4,12 +4,14 @@ import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleCondi
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRoleCondition;
import dev.sheldan.abstracto.assignableroles.repository.AssignableRoleConditionRepository;
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 AssignableRoleConditionManagementServiceBean implements AssignableRoleConditionManagementService {
@Autowired
@@ -23,12 +25,14 @@ public class AssignableRoleConditionManagementServiceBean implements AssignableR
.type(type)
.conditionValue(value)
.build();
log.info("Creating condition of type {} for assignable role {}", assignableRole.getId(), type);
assignableRole.getConditions().add(condition);
return repository.save(condition);
}
@Override
public void deleteAssignableRoleCondition(AssignableRoleCondition condition) {
log.info("Deleting condition {}.", condition.getId());
repository.delete(condition);
}

View File

@@ -2,6 +2,8 @@ package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import dev.sheldan.abstracto.assignableroles.repository.AssignableRoleRepository;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.FullEmote;
@@ -15,6 +17,8 @@ import net.dv8tion.jda.api.entities.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Slf4j
public class AssignableRoleManagementServiceBean implements AssignableRoleManagementService {
@@ -51,8 +55,6 @@ public class AssignableRoleManagementServiceBean implements AssignableRoleManage
return roleToAdd;
}
@Override
public AssignableRole getByAssignableRoleId(Long assignableRoleId) {
return repository.findById(assignableRoleId).orElseThrow(() -> new AbstractoRunTimeException("Assignable role not found"));
@@ -60,7 +62,14 @@ public class AssignableRoleManagementServiceBean implements AssignableRoleManage
@Override
public void deleteAssignableRole(AssignableRole assignableRole) {
assignableRole.getAssignablePlace().getAssignableRoles().remove(assignableRole);
assignableRole.setAssignablePlace(null);
repository.delete(assignableRole);
}
@Override
public List<AssignableRole> getAssignableRolesFromAssignableUserWithPlaceType(AssignedRoleUser user, AssignableRolePlaceType type) {
return repository.findByAssignedUsersContainingAndAssignablePlace_Type(user, type);
}
}

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRolePlaceNotFoundException;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.repository.AssignableRolePlaceRepository;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
@@ -20,12 +21,18 @@ public class AssignableRolePlaceManagementServiceBean implements AssignableRoleP
private AssignableRolePlaceRepository repository;
@Override
public AssignableRolePlace createPlace(String name, AChannel channel, String text) {
public AssignableRolePlace createPlace(String name, AChannel channel, String text, AssignableRolePlaceType type) {
boolean unique = false;
if(type.equals(AssignableRolePlaceType.BOOSTER)) {
unique = true;
}
AssignableRolePlace place = AssignableRolePlace
.builder()
.channel(channel)
.server(channel.getServer())
.text(text)
.uniqueRoles(unique)
.type(type)
.key(name)
.build();
log.info("Creating assignable role place in channel {} on server {}.", channel.getId(), channel.getServer().getId());

View File

@@ -11,6 +11,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
@@ -43,6 +44,18 @@ public class AssignedRoleUserManagementServiceBean implements AssignedRoleUserMa
removeAssignedRoleFromUser(assignableRole, user);
}
@Override
public void removeAssignedRoleFromUsers(AssignableRole assignableRole, List<AssignedRoleUser> users) {
log.info("Clearing all assignable role {} for {} users.", assignableRole.getId(), users.size());
assignableRole.getAssignedUsers().removeAll(users);
users.forEach(roleUser -> roleUser.getRoles().remove(assignableRole));
}
@Override
public void removeAssignedRoleFromUsers(AssignableRole assignableRole) {
removeAssignedRoleFromUsers(assignableRole, assignableRole.getAssignedUsers());
}
@Override
public void removeAssignedRoleFromUser(AssignableRole assignableRole, AssignedRoleUser user) {
assignableRole.getAssignedUsers().remove(user);

View File

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

View File

@@ -0,0 +1,15 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="assignable_role_place-add_type">
<addColumn tableName="assignable_role_place">
<column name="type" type="VARCHAR2(128)" defaultValue="DEFAULT"/>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class BoosterAssignableRolePlaceMemberNotBoostingException extends AbstractoTemplatableException {
public BoosterAssignableRolePlaceMemberNotBoostingException() {
super("Clicking member does not boost");
}
@Override
public String getTemplateName() {
return "assignable_role_booster_place_member_not_boosting_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -85,4 +85,8 @@ public class AssignableRolePlace implements Serializable {
@Column(name = "updated", insertable = false, updatable = false)
private Instant updated;
@Enumerated(EnumType.STRING)
@Column(name = "type")
private AssignableRolePlaceType type;
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.assignableroles.model.database;
import dev.sheldan.abstracto.core.command.execution.CommandParameterKey;
import lombok.Getter;
@Getter
public enum AssignableRolePlaceType implements CommandParameterKey {
DEFAULT, BOOSTER
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.assignableroles.model.template;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.core.models.template.display.ChannelDisplay;
import lombok.Builder;
import lombok.Getter;
@@ -19,6 +20,7 @@ public class AssignableRolePlaceConfig {
private String placeText;
private ChannelDisplay channelDisplay;
private Boolean uniqueRoles;
private AssignableRolePlaceType type;
/**
* The {@link AssignableRolePlaceConfig roles} which are contained in this {@link AssignableRolePlace}
*/

View File

@@ -2,17 +2,21 @@ package dev.sheldan.abstracto.assignableroles.service;
import dev.sheldan.abstracto.assignableroles.config.AssignableRolePlaceParameterKey;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.model.template.AssignablePlaceOverview;
import dev.sheldan.abstracto.assignableroles.model.template.AssignableRolePlaceConfig;
import dev.sheldan.abstracto.core.models.FullEmote;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.TextChannel;
import java.util.concurrent.CompletableFuture;
public interface AssignableRolePlaceService {
void createAssignableRolePlace(String name, AChannel channel, String text);
void createAssignableRolePlace(String name, AChannel channel, String text, AssignableRolePlaceType type);
CompletableFuture<Void> addRoleToAssignableRolePlace(AServer server, String placeName, Role role, FullEmote emote, String description);
@@ -42,7 +46,7 @@ public interface AssignableRolePlaceService {
void multipleAssignableRolePlace(AssignableRolePlace place);
CompletableFuture<Void> showAssignablePlaceConfig(AServer server, String name, TextChannel channel);
AssignableRolePlaceConfig getAssignableRolePlaceConfig(Guild guild, String name);
CompletableFuture<Void> moveAssignableRolePlace(AServer server, String name, TextChannel newChannel);
@@ -52,5 +56,5 @@ public interface AssignableRolePlaceService {
CompletableFuture<Void> changeConfiguration(AServer server, String name, AssignableRolePlaceParameterKey keyToChange, String newValue);
CompletableFuture<Void> showAllAssignableRolePlaces(AServer server, TextChannel channel);
AssignablePlaceOverview getAssignableRolePlaceOverview(Guild guild);
}

View File

@@ -2,11 +2,13 @@ package dev.sheldan.abstracto.assignableroles.service;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
@@ -22,6 +24,8 @@ public interface AssignableRoleService {
*/
CompletableFuture<Void> assignAssignableRoleToUser(Long assignableRoleId, Member member);
CompletableFuture<Void> assignAssignableRoleToUser(Role role, Member member);
void assignableRoleConditionFailure();
/**
* Clears all {@link AssignableRole assignableRoles} which are currently given to the {@link AUserInAServer user} of a certain
@@ -39,6 +43,7 @@ public interface AssignableRoleService {
* has been removed from the {@link Member member}
*/
CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, Member member);
CompletableFuture<Void> removeAssignableRoleFromUser(Role role, Member member);
/**
* Removes the {@link AssignableRole role} from the given {@link Member member}
@@ -67,4 +72,6 @@ public interface AssignableRoleService {
AssignableRole getAssignableRoleInPlace(AssignableRolePlace place, ARole role);
AssignableRole getAssignableRoleInPlace(AssignableRolePlace place, Long roleId);
void removeAssignableRolesFromAssignableRoleUser(List<AssignableRole> roles, AssignedRoleUser roleUser);
}

View File

@@ -2,14 +2,19 @@ package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import dev.sheldan.abstracto.core.models.FullEmote;
import dev.sheldan.abstracto.core.models.database.ComponentPayload;
import net.dv8tion.jda.api.entities.Role;
import java.util.List;
public interface AssignableRoleManagementService {
AssignableRole addRoleToPlace(FullEmote emote, Role role, String description, AssignableRolePlace place, ComponentPayload componentPayload);
AssignableRole getByAssignableRoleId(Long assignableRoleId);
void deleteAssignableRole(AssignableRole assignableRole);
List<AssignableRole> getAssignableRolesFromAssignableUserWithPlaceType(AssignedRoleUser user, AssignableRolePlaceType type);
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
@@ -9,7 +10,7 @@ import java.util.Optional;
public interface AssignableRolePlaceManagementService {
AssignableRolePlace createPlace(String name, AChannel channel, String text);
AssignableRolePlace createPlace(String name, AChannel channel, String text, AssignableRolePlaceType type);
boolean doesPlaceExist(AServer server, String name);

View File

@@ -6,6 +6,7 @@ import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import java.util.List;
import java.util.Optional;
/**
@@ -25,6 +26,8 @@ public interface AssignedRoleUserManagementService {
* @param aUserInAServer The {@link AUserInAServer user} from whom the {@link AssignableRole role} should be removed
*/
void removeAssignedRoleFromUser(AssignableRole assignableRole, AUserInAServer aUserInAServer);
void removeAssignedRoleFromUsers(AssignableRole assignableRole, List<AssignedRoleUser> users);
void removeAssignedRoleFromUsers(AssignableRole assignableRole);
/**
* Removes the given {@link AssignableRole assignableFrom} from the given {@link AssignedRoleUser user}.

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.3.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -36,7 +36,7 @@ public class ActivityServiceBean implements ActivityService {
List<CustomActivity> activities = activityManagementService.getAllActivities();
if(!activities.isEmpty()) {
CustomActivity chosen = activities.get(secureRandom.nextInt(activities.size()));
log.info("Chosen {} activity.", chosen.getId());
log.info("Chosen activity {}.", chosen.getId());
switchToActivity(chosen);
} else {
log.info("No activities configured.");

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>dynamic-activity</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -46,7 +46,9 @@ public class HasLevelCondition implements SystemCondition {
log.info("Evaluating has level condition for user {} in server {} with level {}.",
userInServer.getUserReference().getId(), userInServer.getServerReference().getId(), level);
AUserExperience user = userExperienceManagementService.findUserInServer(userInServer);
return user.getCurrentLevel() != null && user.getCurrentLevel().getLevel() >= level;
boolean conditionResult = user.getCurrentLevel() != null && user.getCurrentLevel().getLevel() >= level;
log.info("Condition evaluated to {}", conditionResult);
return conditionResult;
}
log.info("No user in server object was found. Evaluating to false.");

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking</artifactId>
<version>1.3.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -168,18 +168,39 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
.stream()
.map(EmbeddedMessage::getEmbeddingMessageId)
.collect(Collectors.toList());
List<String> componentPayloadsToDelete = embeddedMessages
.stream()
.map(EmbeddedMessage::getDeletionComponentId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
List<CompletableFuture<Message>> reactionMessageFutures = messageService.retrieveMessages(reactionChannelMessages);
List<CompletableFuture<Message>> buttonMessageFutures = messageService.retrieveMessages(buttonChannelMessages);
CompletableFutureList<Message> reactionFutureList = new CompletableFutureList<>(reactionMessageFutures);
CompletableFutureList<Message> buttonFutureList = new CompletableFutureList<>(buttonMessageFutures);
return reactionFutureList.getMainFuture()
.handle((unused, throwable) -> self.removeReactions(reactionFutureList.getObjects()))
.handle((unused, throwable) -> {
if(throwable != null) {
log.warn("Embedded messages reaction message loading failed.", throwable);
}
return self.removeReactions(reactionFutureList.getObjects());
})
.thenCompose(Function.identity())
.thenCompose(unused -> buttonFutureList.getMainFuture())
.handle((unused, throwable) -> self.removeButtons(buttonFutureList.getObjects()))
.handle((unused, throwable) -> {
if(throwable != null) {
log.warn("Embedded messages button message loading failed.", throwable);
}
return self.removeButtons(buttonFutureList.getObjects());
})
// deleting the messages from db regardless of exceptions, at most the reaction remains
.thenCompose(Function.identity())
.whenComplete((unused, throwable) -> self.deleteEmbeddedMessages(embeddedMessagesHandled))
.whenComplete((unused, throwable) -> {
if(throwable != null) {
log.warn("Embedded message button clearing failed.", throwable);
}
self.deleteEmbeddedMessages(embeddedMessagesHandled, componentPayloadsToDelete);
})
.exceptionally(throwable -> {
log.error("Failed to clean up embedded messages.", throwable);
return null;
@@ -216,8 +237,9 @@ public class MessageEmbedServiceBean implements MessageEmbedService {
}
@Transactional
public void deleteEmbeddedMessages(List<Long> embeddedMessagesToDelete) {
public void deleteEmbeddedMessages(List<Long> embeddedMessagesToDelete, List<String> componentPayloadsToDelete) {
messageEmbedPostManagementService.deleteEmbeddedMessagesViaId(embeddedMessagesToDelete);
componentPayloadManagementService.deletePayloads(componentPayloadsToDelete);
}
@Transactional

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>link-embed</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -14,6 +14,7 @@ import dev.sheldan.abstracto.core.service.management.UserInServerManagementServi
import dev.sheldan.abstracto.core.utils.ContextUtils;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.logging.config.LoggingFeatureDefinition;
import dev.sheldan.abstracto.logging.config.LoggingPostTarget;
import dev.sheldan.abstracto.logging.model.template.MessageDeletedAttachmentLog;
@@ -81,11 +82,14 @@ public class MessageDeleteLogListener implements AsyncMessageDeletedListener {
.member(authorMember)
.build();
MessageToSend message = templateService.renderEmbedTemplate(MESSAGE_DELETED_TEMPLATE, logModel, messageFromCache.getServerId());
postTargetService.sendEmbedInPostTarget(message, LoggingPostTarget.DELETE_LOG, messageFromCache.getServerId());
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, LoggingPostTarget.DELETE_LOG, messageFromCache.getServerId())).exceptionally(throwable -> {
log.error("Failed to send message deleted log.", throwable);
return null;
});
if(messageFromCache.getAttachments() != null){
log.debug("Notifying about deletions of {} attachments.", messageFromCache.getAttachments().size());
for (int i = 0; i < messageFromCache.getAttachments().size(); i++) {
MessageDeletedAttachmentLog log = MessageDeletedAttachmentLog
MessageDeletedAttachmentLog attachmentLogModel = MessageDeletedAttachmentLog
.builder()
.imageUrl(messageFromCache.getAttachments().get(i).getProxyUrl())
.counter(i + 1)
@@ -93,8 +97,12 @@ public class MessageDeleteLogListener implements AsyncMessageDeletedListener {
.channel(textChannel)
.member(authorMember)
.build();
MessageToSend attachmentEmbed = templateService.renderEmbedTemplate(MESSAGE_DELETED_ATTACHMENT_TEMPLATE, log, messageFromCache.getServerId());
postTargetService.sendEmbedInPostTarget(attachmentEmbed, LoggingPostTarget.DELETE_LOG, messageFromCache.getServerId());
MessageToSend attachmentEmbed = templateService.renderEmbedTemplate(MESSAGE_DELETED_ATTACHMENT_TEMPLATE, attachmentLogModel, messageFromCache.getServerId());
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(attachmentEmbed, LoggingPostTarget.DELETE_LOG, messageFromCache.getServerId()))
.exceptionally(throwable -> {
log.error("Failed to send message deleted log.", throwable);
return null;
});
}
}
}

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>logging</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -40,8 +40,7 @@ public class Ban extends AbstractConditionableCommand {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
User user = (User) parameters.get(0);
String defaultReason = templateService.renderSimpleTemplate(BAN_DEFAULT_REASON_TEMPLATE, commandContext.getGuild().getIdLong());
String reason = parameters.size() == 2 ? (String) parameters.get(1) : defaultReason;
String reason = (String) parameters.get(1);
return banService.banUser(user, reason, commandContext.getAuthor(), commandContext.getMessage())
.thenApply(aVoid -> CommandResult.fromSuccess());
@@ -51,7 +50,7 @@ public class Ban extends AbstractConditionableCommand {
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").templated(true).type(User.class).build());
parameters.add(Parameter.builder().name("reason").templated(true).type(String.class).optional(true).remainder(true).build());
parameters.add(Parameter.builder().name("reason").templated(true).type(String.class).remainder(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
List<EffectConfig> effectConfig = Arrays.asList(EffectConfig.builder().position(0).effectKey(BAN_EFFECT_KEY).build());
return CommandConfiguration.builder()

View File

@@ -0,0 +1,78 @@
package dev.sheldan.abstracto.moderation.command;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.condition.CommandCondition;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.EffectConfig;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.service.BanService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.moderation.service.BanService.BAN_EFFECT_KEY;
@Component
@Slf4j
public class SoftBan extends AbstractConditionableCommand {
@Autowired
private BanService banService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
User user = (User) parameters.get(0);
Duration delDays = Duration.ofDays(7);
if(parameters.size() > 1) {
delDays = (Duration) parameters.get(1);
}
return banService.softBanUser(commandContext.getGuild(), user, delDays)
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").templated(true).type(User.class).build());
parameters.add(Parameter.builder().name("delDays").templated(true).type(Duration.class).optional(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
List<EffectConfig> effectConfig = Arrays.asList(EffectConfig.builder().position(0).effectKey(BAN_EFFECT_KEY).build());
return CommandConfiguration.builder()
.name("softBan")
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.effects(effectConfig)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return ModerationFeatureDefinition.MODERATION;
}
@Override
public List<CommandCondition> getConditions() {
List<CommandCondition> conditions = super.getConditions();
conditions.add(immuneUserCondition);
return conditions;
}
}

View File

@@ -50,6 +50,7 @@ public class UserBannedListener implements AsyncUserBannedListener {
.queue(auditLogEntries -> {
if(auditLogEntries.isEmpty()) {
log.info("Did not find recent bans in guild {}.", model.getServerId());
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
return;
}
Optional<AuditLogEntry> banEntryOptional = auditLogEntries
@@ -65,6 +66,9 @@ public class UserBannedListener implements AsyncUserBannedListener {
log.info("Did not find the banned user in the most recent bans for guild {}. Not adding audit log information.", model.getServerId());
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
}
}, throwable -> {
log.error("Retrieving bans for guild {} failed - logging ban regardless.", model.getServerId());
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
});
return DefaultListenerResult.PROCESSED;
}

View File

@@ -16,6 +16,7 @@ import net.dv8tion.jda.api.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -58,7 +59,7 @@ public class BanServiceBean implements BanService {
.commandMessage(message)
.reason(reason)
.build();
CompletableFuture<Void> banFuture = banUser(member.getGuild(), member.getUser(), reason);
CompletableFuture<Void> banFuture = banUser(member.getGuild(), member.getUser(), 0, reason);
CompletableFuture<Void> messageFuture = sendBanLogMessage(banLog, member.getGuild().getIdLong(), BAN_LOG_TEMPLATE);
return CompletableFuture.allOf(banFuture, messageFuture);
}
@@ -81,7 +82,7 @@ public class BanServiceBean implements BanService {
.thenAccept(message1 -> log.info("Notified about not being able to send ban notification in server {} and channel {} based on message {} from user {}."
, message.getGuild().getIdLong(), message.getChannel().getIdLong(), message.getIdLong(), message.getAuthor().getIdLong()));
}
CompletableFuture<Void> banFuture = banUser(guild, user, reason);
CompletableFuture<Void> banFuture = banUser(guild, user, 0, reason);
CompletableFuture<Void> messageFuture = sendBanLogMessage(banLog, guild.getIdLong(), BAN_LOG_TEMPLATE);
CompletableFuture.allOf(banFuture, messageFuture)
.thenAccept(unused1 -> returningFuture.complete(null))
@@ -111,10 +112,29 @@ public class BanServiceBean implements BanService {
.bannedUser(user)
.unBanningMember(unBanningMember)
.build();
return unBanUser(guild, user)
return unbanUser(guild, user)
.thenCompose(unused -> self.sendUnBanLogMessage(banLog, guild.getIdLong(), UN_BAN_LOG_TEMPLATE));
}
@Override
public CompletableFuture<Void> banUser(Guild guild, User user, Integer deletionDays, String reason) {
log.info("Banning user {} in guild {}.", user.getIdLong(), guild.getId());
return guild.ban(user, deletionDays, reason).submit();
}
@Override
public CompletableFuture<Void> unbanUser(Guild guild, User user) {
log.info("Unbanning user {} in guild {}.", user.getIdLong(), guild.getId());
return guild.unban(user).submit();
}
@Override
public CompletableFuture<Void> softBanUser(Guild guild, User user, Duration delDays) {
Long days = delDays.toDays();
return banUser(guild, user, days.intValue(), "")
.thenCompose(unused -> unbanUser(guild, user));
}
public CompletableFuture<Void> sendBanLogMessage(BanLog banLog, Long guildId, String template) {
CompletableFuture<Void> completableFuture;
MessageToSend banLogMessage = templateService.renderEmbedTemplate(template, banLog, guildId);
@@ -132,14 +152,4 @@ public class BanServiceBean implements BanService {
completableFuture = FutureUtils.toSingleFutureGeneric(notificationFutures);
return completableFuture;
}
private CompletableFuture<Void> banUser(Guild guild, User user, String reason) {
log.info("Banning user {} in guild {}.", user.getIdLong(), guild.getId());
return guild.ban(user, 0, reason).submit();
}
private CompletableFuture<Void> unBanUser(Guild guild, User user) {
log.info("Unbanning user {} in guild {}.", user.getIdLong(), guild.getId());
return guild.unban(user).submit();
}
}

View File

@@ -378,4 +378,10 @@ public class MuteServiceBean implements MuteService {
completelyUnMuteUser(userInServerManagementService.loadOrCreateUser(member));
}
@Override
public CompletableFuture<Void> muteMemberWithoutContext(Member member) {
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
return applyMuteRole(aUserInAServer);
}
}

View File

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

View File

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

View File

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

View File

@@ -10,4 +10,5 @@
<include file="1.2.11-moderation/collection.xml" relativeToChangelogFile="true"/>
<include file="1.2.15/collection.xml" relativeToChangelogFile="true"/>
<include file="1.2.16/collection.xml" relativeToChangelogFile="true"/>
<include file="1.3.4/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -31,36 +31,16 @@ public class BanTest {
@Mock
private BanService banService;
@Mock
private TemplateService templateService;
@Captor
private ArgumentCaptor<Member> banLogModelCaptor;
private static final String REASON = "reason";
private static final Long SERVER_ID = 1L;
@Mock
private User bannedMember;
@Test
public void testBanWithDefaultReason() {
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(bannedMember));
when(parameters.getGuild().getIdLong()).thenReturn(SERVER_ID);
when(templateService.renderSimpleTemplate(Ban.BAN_DEFAULT_REASON_TEMPLATE, SERVER_ID)).thenReturn(REASON);
when(banService.banUser(eq(bannedMember), eq(REASON), banLogModelCaptor.capture(), any(Message.class))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
Member banningMember = banLogModelCaptor.getValue();
Assert.assertEquals(parameters.getAuthor(), banningMember);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
}
@Test
public void testBanWithReason() {
String customReason = "reason2";
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(bannedMember, customReason));
when(parameters.getGuild().getIdLong()).thenReturn(SERVER_ID);
when(templateService.renderSimpleTemplate(Ban.BAN_DEFAULT_REASON_TEMPLATE, SERVER_ID)).thenReturn(REASON);
when(banService.banUser(eq(bannedMember), eq(customReason), banLogModelCaptor.capture(), any(Message.class))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
Member banningMember = banLogModelCaptor.getValue();

View File

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

View File

@@ -1,9 +1,11 @@
package dev.sheldan.abstracto.moderation.service;
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.User;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
public interface BanService {
@@ -11,4 +13,7 @@ public interface BanService {
CompletableFuture<Void> banMember(Member member, String reason, Member banningMember, Message message);
CompletableFuture<Void> banUser(User user, String reason, Member banningMember, Message message);
CompletableFuture<Void> unBanUser(User user, Member unBanningUser);
CompletableFuture<Void> banUser(Guild guild, User user, Integer deletionDays, String reason);
CompletableFuture<Void> unbanUser(Guild guild, User user);
CompletableFuture<Void> softBanUser(Guild guild, User user, Duration delDays);
}

View File

@@ -23,4 +23,5 @@ public interface MuteService {
CompletableFuture<Void> endMute(Long muteId, Long serverId);
void completelyUnMuteUser(AUserInAServer aUserInAServer);
void completelyUnMuteMember(Member member);
CompletableFuture<Void> muteMemberWithoutContext(Member member);
}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.3.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -39,6 +39,7 @@ import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.exceptions.InsufficientPermissionException;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -62,6 +63,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* The config key to use for the ID of the category to create {@link MessageChannel} in
*/
public static final String MODMAIL_CATEGORY = "modmailCategory";
public static final String TEXT_CHANNEL_NAME_TEMPLATE_KEY = "modMail_channel_name";
/**
* The template key used for default mod mail exceptions
*/
@@ -189,7 +191,16 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
metricService.incrementCounter(MODMAIL_THREAD_CREATED_COUNTER);
User user = member.getUser();
log.info("Creating modmail channel for user {} in category {} on server {}.", user.getId(), categoryId, serverId);
CompletableFuture<TextChannel> textChannelFuture = channelService.createTextChannel(user.getName() + user.getDiscriminator(), server, categoryId);
ModMailChannelNameModel model = ModMailChannelNameModel
.builder()
.serverId(serverId)
.userId(member.getIdLong())
.randomText(RandomStringUtils.randomAlphanumeric(25))
.uuid(UUID.randomUUID().toString())
.currentDate(Instant.now())
.build();
String channelName = templateService.renderTemplate(TEXT_CHANNEL_NAME_TEMPLATE_KEY, model, serverId);
CompletableFuture<TextChannel> textChannelFuture = channelService.createTextChannel(channelName, server, categoryId);
return textChannelFuture.thenCompose(channel -> {
undoActions.add(UndoActionInstance.getChannelDeleteAction(serverId, channel.getIdLong()));
return self.performModMailThreadSetup(member, initialMessage, channel, userInitiated, undoActions, feedBackChannel);
@@ -361,7 +372,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
future.completeExceptionally(exception);
return future;
}
}).exceptionally(throwable -> {
}).exceptionally(throwable -> {
log.error("Failed to load member {} for modmail in server {}.", userId, chosenServerId, throwable);
return null;
});
@@ -537,7 +548,6 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Transactional
public CompletableFuture<Void> relayMessageToDm(Long modmailThreadId, String text, Message replyCommandMessage, boolean anonymous, MessageChannel feedBack, List<UndoActionInstance> undoActions, Member targetMember) {
log.info("Relaying message {} to user {} in modmail thread {} on server {}.", replyCommandMessage.getId(), targetMember.getId(), modmailThreadId, targetMember.getGuild().getId());
AUserInAServer moderator = userInServerManagementService.loadOrCreateUser(replyCommandMessage.getMember());
metricService.incrementCounter(MDOMAIL_THREAD_MESSAGE_SENT);
ModMailThread modMailThread = modMailThreadManagementService.getById(modmailThreadId);
FullUserInServer fullThreadUser = FullUserInServer
@@ -556,9 +566,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
log.debug("Message is sent anonymous.");
modMailModeratorReplyModelBuilder.moderator(memberService.getBotInGuild(modMailThread.getServer()));
} else {
// should be loaded, because we are currently processing a command caused by the message
Member moderatorMember = memberService.getMemberInServer(moderator);
modMailModeratorReplyModelBuilder.moderator(moderatorMember);
modMailModeratorReplyModelBuilder.moderator(replyCommandMessage.getMember());
}
ModMailModeratorReplyModel modMailUserReplyModel = modMailModeratorReplyModelBuilder.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(MODMAIL_STAFF_MESSAGE_TEMPLATE_KEY, modMailUserReplyModel, modMailThread.getServer().getId());
@@ -570,7 +578,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
sameThreadMessageFuture = CompletableFuture.completedFuture(null);
}
return CompletableFuture.allOf(future, sameThreadMessageFuture).thenAccept(avoid ->
self.saveSendMessagesAndUpdateState(modmailThreadId, anonymous, moderator, future.join(), replyCommandMessage, sameThreadMessageFuture.join())
self.saveSendMessagesAndUpdateState(modmailThreadId, anonymous, future.join(), replyCommandMessage, sameThreadMessageFuture.join())
);
}
@@ -752,11 +760,11 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.build();
List<CompletableFuture<Message>> updateMessageFutures = channelService.sendEmbedTemplateInTextChannelList(MODMAIL_CLOSE_PROGRESS_TEMPLATE_KEY, progressModel, channel);
return FutureUtils.toSingleFutureGeneric(updateMessageFutures)
.thenApply(updateMessage -> self.logMessages(modMailThreadId, messages, context, updateMessageFutures.get(0).join()));
.thenCompose(updateMessage -> self.logMessages(modMailThreadId, messages, context, updateMessageFutures.get(0).join()));
}
@Transactional
public CompletableFutureList<Message> logMessages(Long modMailThreadId, ModmailLoggingThreadMessages messages, ClosingContext context, Message updateMessage) {
public CompletableFuture<CompletableFutureList<Message>> logMessages(Long modMailThreadId, ModmailLoggingThreadMessages messages, ClosingContext context, Message updateMessage) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get();
@@ -789,10 +797,11 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
List<CompletableFuture<Message>> completableFutures = new ArrayList<>();
log.debug("Sending close header and individual mod mail messages to mod mail log target for thread {}.", modMailThreadId);
CompletableFuture<Message> headerFuture = loadUserAndSendClosingHeader(modMailThread, context);
// TODO the header might end up later on discord servers, this look out of order
completableFutures.add(headerFuture);
completableFutures.addAll(self.sendMessagesToPostTarget(modMailThread, loggedMessages, updateMessage));
return new CompletableFutureList<>(completableFutures);
return headerFuture.thenApply(message -> {
completableFutures.addAll(self.sendMessagesToPostTarget(modMailThreadId, loggedMessages, updateMessage));
return new CompletableFutureList<>(completableFutures);
});
} else {
throw new ModMailThreadNotFoundException(modMailThreadId);
}
@@ -803,11 +812,10 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.builder()
.closingMember(closingContext.getClosingMember())
.note(closingContext.getNote())
.silently(closingContext.getNotifyUser())
.silently(!closingContext.getNotifyUser())
.messageCount(modMailThread.getMessages().size())
.startDate(modMailThread.getCreated())
.serverId(modMailThread.getServer().getId())
.silently(!closingContext.getNotifyUser())
.userId(modMailThread.getUser().getUserReference().getId())
.build();
return userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenApply(user -> {
@@ -843,11 +851,11 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
/**
* Renders the retrieved {@link Message} which are in {@link ModMailLoggedMessageModel} into {@link MessageToSend} and
* sends this to the appropriate logging {@link PostTarget}
* @param modMailThread The {@link ModMailThread} to which the loaded messages belong to
* @param modMailThreadId The ID of {@link ModMailThread} to which the loaded messages belong to
* @param loadedMessages The list of {@link ModMailLoggedMessageModel} which can be rendered
* @return A list of {@link CompletableFuture} which represent each of the messages being send to the {@link PostTarget}
*/
public List<CompletableFuture<Message>> sendMessagesToPostTarget(ModMailThread modMailThread, List<ModMailLoggedMessageModel> loadedMessages, Message updateMessage) {
public List<CompletableFuture<Message>> sendMessagesToPostTarget(Long modMailThreadId, List<ModMailLoggedMessageModel> loadedMessages, Message updateMessage) {
List<CompletableFuture<Message>> messageFutures = new ArrayList<>();
ClosingProgressModel progressModel = ClosingProgressModel
.builder()
@@ -857,9 +865,9 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
loadedMessages = loadedMessages.stream().sorted(Comparator.comparing(o -> o.getMessage().getTimeCreated())).collect(Collectors.toList());
for (int i = 0; i < loadedMessages.size(); i++) {
ModMailLoggedMessageModel message = loadedMessages.get(i);
log.debug("Sending message {} of modmail thread {} to modmail log post target.", modMailThread.getId(), message.getMessage().getId());
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_close_logged_message", message, modMailThread.getServer().getId());
List<CompletableFuture<Message>> logFuture = postTargetService.sendEmbedInPostTarget(messageToSend, ModMailPostTargets.MOD_MAIL_LOG, modMailThread.getServer().getId());
log.debug("Sending message {} of modmail thread {} to modmail log post target.", modMailThreadId, message.getMessage().getId());
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_close_logged_message", message, updateMessage.getGuild().getIdLong());
List<CompletableFuture<Message>> logFuture = postTargetService.sendEmbedInPostTarget(messageToSend, ModMailPostTargets.MOD_MAIL_LOG, updateMessage.getGuild().getIdLong());
if(i != 0 && (i % 10) == 0) {
progressModel.setLoggedMessages(i);
messageService.editMessageWithNewTemplate(updateMessage, MODMAIL_CLOSE_PROGRESS_TEMPLATE_KEY, progressModel);
@@ -874,15 +882,15 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* and updates the state of the {@link ModMailThread}.
* @param modMailThreadId The ID of the {@link ModMailThread} for which the messages were sent for
* @param anonymous Whether or not the messages were send anonymous
* @param moderator The original {@link AUserInAServer} which authored the messages
* @param createdMessageInDM The {@link Message message} which was sent to the private channel with the {@link User user}
* @param modMailThreadMessage The {@link Message message} which was sent in the channel representing the {@link ModMailThread thread}. Might be null.
* @param replyCommandMessage The {@link Message message} which contained the command used to reply to the user
* @throws ModMailThreadNotFoundException in case the {@link ModMailThread} is not found by the ID
*/
@Transactional
public void saveSendMessagesAndUpdateState(Long modMailThreadId, Boolean anonymous, AUserInAServer moderator, Message createdMessageInDM, Message replyCommandMessage, Message modMailThreadMessage) {
public void saveSendMessagesAndUpdateState(Long modMailThreadId, Boolean anonymous, Message createdMessageInDM, Message replyCommandMessage, Message modMailThreadMessage) {
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
AUserInAServer moderator = userInServerManagementService.loadOrCreateUser(replyCommandMessage.getMember());
if(modMailThreadOpt.isPresent()) {
ModMailThread modMailThread = modMailThreadOpt.get();
log.debug("Adding (anonymous: {}) message {} of moderator to modmail thread {} and setting state to {}.", anonymous, createdMessageInDM.getId(), modMailThreadId, ModMailThreadState.MOD_REPLIED);

View File

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

View File

@@ -0,0 +1,18 @@
package dev.sheldan.abstracto.modmail.model.template;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.Instant;
@Getter
@Setter
@Builder
public class ModMailChannelNameModel {
private Long serverId;
private Long userId;
private Instant currentDate;
private String randomText;
private String uuid;
}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.3.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -29,6 +29,7 @@
<module>profanity-filter</module>
<module>voice-channel-context</module>
<module>dynamic-activity</module>
<module>anti-raid</module>
</modules>
</project>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</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.2</version>
<version>1.3.7</version>
</parent>
<modelVersion>4.0.0</modelVersion>

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