[AB-106] adding ability to specify deletion duration on ban command

removing banDelete command
updating jda version
This commit is contained in:
Sheldan
2023-09-26 00:56:15 +02:00
parent f12581f322
commit f97fe42e65
12 changed files with 102 additions and 168 deletions

View File

@@ -12,7 +12,7 @@ An example implementation of this bot can be seen [here](https://github.com/Shel
## Technologies
* [JDA](https://github.com/DV8FromTheWorld/JDA/) The Discord API Wrapper in the version 5.0.0-beta.5
* [JDA](https://github.com/DV8FromTheWorld/JDA/) The Discord API Wrapper in the version 5.0.0-beta.13
* [Spring boot](https://github.com/spring-projects/spring-boot) is used as a framework to create standalone application in Java with Java EE methods. (including dependency injection and more)
* [Hibernate](https://github.com/hibernate/hibernate-orm) is used as a reference implementation of JPA.
* [Freemarker](https://github.com/apache/freemarker) is used as a templating engine. This is used to provide internationalization for user facing text and enable dynamic embed configuration.

View File

@@ -3,7 +3,6 @@ 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.*;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
@@ -12,20 +11,20 @@ import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.ParseUtils;
import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
import dev.sheldan.abstracto.moderation.config.ModerationSlashCommandNames;
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.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -39,6 +38,7 @@ public class Ban extends AbstractConditionableCommand {
private static final String BAN_COMMAND = "ban";
private static final String REASON_PARAMETER = "reason";
private static final String DELETION_DURATION_PARAMETER = "deletionDuration";
private static final String USER_PARAMETER = "user";
public static final String BAN_NOTIFICATION_NOT_POSSIBLE = "ban_notification_not_possible";
private static final String BAN_RESPONSE = "ban_response";
@@ -46,9 +46,6 @@ public class Ban extends AbstractConditionableCommand {
@Autowired
private BanService banService;
@Autowired
private ChannelService channelService;
@Autowired
private TemplateService templateService;
@@ -61,34 +58,19 @@ public class Ban extends AbstractConditionableCommand {
@Autowired
private UserService userService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
User user = (User) parameters.get(0);
String reason = (String) parameters.get(1);
Guild guild = commandContext.getGuild();
Message message = commandContext.getMessage();
Member banningMember = commandContext.getAuthor();
return banService.banUserWithNotification(user, reason, commandContext.getAuthor(), 0)
.thenCompose(banResult -> {
if(banResult == NOTIFICATION_FAILED) {
String errorNotification = templateService.renderSimpleTemplate(BAN_NOTIFICATION_NOT_POSSIBLE, guild.getIdLong());
return channelService.sendTextToChannel(errorNotification, message.getChannel())
.thenAccept(message1 -> log.info("Notified about not being able to send ban notification in server {} and channel {} from user {}."
, guild, message.getChannel().getIdLong(), banningMember.getIdLong()));
} else {
return CompletableFuture.completedFuture(null);
}
})
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String reason = slashCommandParameterService.getCommandOption(REASON_PARAMETER, event, String.class, String.class);
Duration duration;
if(slashCommandParameterService.hasCommandOption(DELETION_DURATION_PARAMETER, event)) {
String durationString = slashCommandParameterService.getCommandOption(DELETION_DURATION_PARAMETER, event, Duration.class, String.class);
duration = ParseUtils.parseDuration(durationString);
} else {
duration = null;
}
if(slashCommandParameterService.hasCommandOptionWithFullType(USER_PARAMETER, event, OptionType.USER)) {
Member member = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, User.class, Member.class);
return banService.banUserWithNotification(member.getUser(), reason, event.getMember(), 0)
return banService.banUserWithNotification(member.getUser(), reason, event.getMember(), duration)
.thenCompose(banResult -> {
if(banResult == NOTIFICATION_FAILED) {
String errorNotification = templateService.renderSimpleTemplate(BAN_NOTIFICATION_NOT_POSSIBLE, event.getGuild().getIdLong());
@@ -102,7 +84,7 @@ public class Ban extends AbstractConditionableCommand {
String userIdStr = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, User.class, String.class);
Long userId = Long.parseLong(userIdStr);
return userService.retrieveUserForId(userId)
.thenCompose(user -> banService.banUserWithNotification(user, reason, event.getMember(), 0))
.thenCompose(user -> banService.banUserWithNotification(user, reason, event.getMember(), duration))
.thenCompose(banResult -> {
if(banResult == NOTIFICATION_FAILED) {
String errorNotification = templateService.renderSimpleTemplate(BAN_NOTIFICATION_NOT_POSSIBLE, event.getGuild().getIdLong());
@@ -128,9 +110,17 @@ public class Ban extends AbstractConditionableCommand {
.name(REASON_PARAMETER)
.templated(true)
.type(String.class)
.remainder(true)
.build();
List<Parameter> parameters = Arrays.asList(userParameter, reasonParameter);
Parameter deletionDurationParameter = Parameter
.builder()
.name(DELETION_DURATION_PARAMETER)
.templated(true)
.type(String.class)
.optional(true)
.build();
List<Parameter> parameters = Arrays.asList(userParameter, reasonParameter, deletionDurationParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
@@ -157,6 +147,7 @@ public class Ban extends AbstractConditionableCommand {
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.slashCommandOnly(true)
.effects(effectConfig)
.supportsEmbedException(true)
.causesReaction(true)

View File

@@ -1,117 +0,0 @@
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.core.service.ChannelService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
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.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.moderation.command.Ban.BAN_NOTIFICATION_NOT_POSSIBLE;
import static dev.sheldan.abstracto.moderation.service.BanService.BAN_EFFECT_KEY;
@Component
@Slf4j
public class BanDelete extends AbstractConditionableCommand {
@Autowired
private BanService banService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
User user = (User) parameters.get(0);
Integer delDays = (Integer) parameters.get(1);
String reason = (String) parameters.get(2);
Guild guild = commandContext.getGuild();
Message message = commandContext.getMessage();
Member banningMember = commandContext.getAuthor();
return banService.banUserWithNotification(user, reason, commandContext.getAuthor(), delDays)
.thenCompose(banResult -> {
String errorNotification = templateService.renderSimpleTemplate(BAN_NOTIFICATION_NOT_POSSIBLE, guild.getIdLong());
return channelService.sendTextToChannel(errorNotification, message.getChannel())
.thenAccept(message1 -> log.info("Notified about not being able to send ban notification in server {} and channel {} from user {}."
, guild, message.getChannel().getIdLong(), banningMember.getIdLong()));
})
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
Parameter userParameter = Parameter
.builder()
.name("user")
.templated(true)
.type(User.class)
.build();
parameters.add(userParameter);
Parameter delDaysParameter = Parameter
.builder()
.name("delDays")
.templated(true)
.type(Integer.class)
.build();
parameters.add(delDaysParameter);
Parameter reasonParameter = Parameter
.builder()
.name("reason")
.templated(true)
.type(String.class)
.remainder(true)
.build();
parameters.add(reasonParameter);
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("banDelete")
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.messageCommandOnly(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

@@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
@Component
@@ -74,19 +75,19 @@ public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionLis
public void handleBanUpdate(InfractionDescriptionEventModel model, CompletableFuture<User> infractionUser, CompletableFuture<Member> infractionCreator, CompletableFuture<DefaultListenerResult> returningFuture) {
Infraction infraction = infractionManagementService.loadInfraction(model.getInfractionId());
GuildMessageChannel messageChannel = channelService.getMessageChannelFromServer(model.getServerId(), infraction.getLogChannel().getId());
Integer deletionDays = infraction
Duration deletionDuration = infraction
.getParameters()
.stream()
.filter(infractionParameter -> infractionParameter.getInfractionParameterId().getName().equals(BanService.INFRACTION_PARAMETER_DELETION_DAYS_KEY))
.findAny()
.map(InfractionParameter::getValue)
.map(Integer::parseInt)
.orElse(0);
.map(Duration::parse)
.orElse(Duration.ZERO);
BanLog banLog = BanLog
.builder()
.bannedUser(infractionUser.isCompletedExceptionally() ? null : infractionUser.join())
.banningMember(infractionCreator.isCompletedExceptionally() ? null : infractionCreator.join())
.deletionDays(deletionDays)
.deletionDuration(deletionDuration)
.reason(model.getNewDescription())
.build();

View File

@@ -60,12 +60,12 @@ public class BanServiceBean implements BanService {
private InfractionService infractionService;
@Override
public CompletableFuture<BanResult> banUserWithNotification(User user, String reason, Member banningMember, Integer deletionDays) {
public CompletableFuture<BanResult> banUserWithNotification(User user, String reason, Member banningMember, Duration deletionDuration) {
BanLog banLog = BanLog
.builder()
.bannedUser(user)
.banningMember(banningMember)
.deletionDays(deletionDays)
.deletionDuration(deletionDuration)
.reason(reason)
.build();
Guild guild = banningMember.getGuild();
@@ -75,20 +75,20 @@ public class BanServiceBean implements BanService {
result[0] = BanResult.NOTIFICATION_FAILED;
return null;
})
.thenCompose(unused -> banUser(guild, user, deletionDays, reason))
.thenCompose(unused -> banUser(guild, user, deletionDuration, reason))
.thenCompose(unused -> sendBanLogMessage(banLog, guild.getIdLong()))
.thenAccept(banLogMessage -> self.evaluateAndStoreInfraction(user, guild, reason, banningMember, banLogMessage, deletionDays))
.thenAccept(banLogMessage -> self.evaluateAndStoreInfraction(user, guild, reason, banningMember, banLogMessage, deletionDuration))
.thenApply(unused -> result[0]);
}
@Transactional
public CompletableFuture<Long> evaluateAndStoreInfraction(User user, Guild guild, String reason, Member banningMember, Message banLogMessage, Integer deletionDays) {
public CompletableFuture<Long> evaluateAndStoreInfraction(User user, Guild guild, String reason, Member banningMember, Message banLogMessage, Duration deletionDuration) {
if(featureFlagService.getFeatureFlagValue(ModerationFeatureDefinition.INFRACTIONS, guild.getIdLong())) {
Long infractionPoints = configService.getLongValueOrConfigDefault(ModerationFeatureConfig.BAN_INFRACTION_POINTS, guild.getIdLong());
AUserInAServer bannedUser = userInServerManagementService.loadOrCreateUser(guild.getIdLong(), user.getIdLong());
AUserInAServer banningUser = userInServerManagementService.loadOrCreateUser(banningMember);
Map<String, String> parameters = new HashMap<>();
parameters.put(INFRACTION_PARAMETER_DELETION_DAYS_KEY, deletionDays.toString());
parameters.put(INFRACTION_PARAMETER_DELETION_DAYS_KEY, deletionDuration.toString());
return infractionService.createInfractionWithNotification(bannedUser, infractionPoints, BAN_INFRACTION_TYPE, reason, banningUser, parameters, banLogMessage)
.thenApply(Infraction::getId);
} else {
@@ -119,9 +119,12 @@ public class BanServiceBean implements BanService {
}
@Override
public CompletableFuture<Void> banUser(Guild guild, User user, Integer deletionDays, String reason) {
public CompletableFuture<Void> banUser(Guild guild, User user, Duration deletionDuration, String reason) {
log.info("Banning user {} in guild {}.", user.getIdLong(), guild.getId());
return guild.ban(user, deletionDays, TimeUnit.DAYS).reason(reason).submit();
if(deletionDuration == null || deletionDuration.isNegative()) {
deletionDuration = Duration.ZERO;
}
return guild.ban(user, (int) deletionDuration.getSeconds(), TimeUnit.SECONDS).reason(reason).submit();
}
@Override
@@ -132,8 +135,7 @@ public class BanServiceBean implements BanService {
@Override
public CompletableFuture<Void> softBanUser(Guild guild, User user, Duration delDays) {
Long days = delDays.toDays();
return banUser(guild, user, days.intValue(), "")
return banUser(guild, user, delDays, "")
.thenCompose(unused -> unbanUser(guild, 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="delete/delete.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,34 @@
<?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="banDeleteCommandId" value="(SELECT id FROM command WHERE name = 'banDelete')"/>
<changeSet author="Sheldan" id="delete-banDelete_command">
<sql >
delete from ${database.defaultSchemaName}.command_in_server_allowed_role
where command_in_server_id
in
(select
command_in_server_id
from ${database.defaultSchemaName}.command_in_server
where command_id = (SELECT id FROM ${database.defaultSchemaName}.command WHERE name = 'banDelete'));
delete from ${database.defaultSchemaName}.command_in_server_alias
where command_in_server_id
in
(select command_in_server_id
from ${database.defaultSchemaName}.command_in_server
where command_id =
(SELECT id FROM ${database.defaultSchemaName}.command WHERE name = 'banDelete'))
</sql>
<delete tableName="command_in_server">
<where>command_id=${banDeleteCommandId}</where>
</delete>
<delete tableName="command">
<where>id=${banDeleteCommandId}</where>
</delete>
</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

@@ -15,4 +15,5 @@
<include file="1.3.10/collection.xml" relativeToChangelogFile="true"/>
<include file="1.4.0/collection.xml" relativeToChangelogFile="true"/>
<include file="1.4.3/collection.xml" relativeToChangelogFile="true"/>
<include file="1.5.10/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -7,6 +7,8 @@ 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;
/**
* Used when rendering the notification when a member was banned. The template is: "ban_log_embed"
@@ -28,5 +30,5 @@ public class BanLog {
*/
private User bannedUser;
private Message commandMessage;
private Integer deletionDays;
private Duration deletionDuration;
}

View File

@@ -10,9 +10,9 @@ public interface BanService {
String BAN_EFFECT_KEY = "ban";
String BAN_INFRACTION_TYPE = "ban";
String INFRACTION_PARAMETER_DELETION_DAYS_KEY = "DELETION_DAYS";
CompletableFuture<BanResult> banUserWithNotification(User user, String reason, Member banningMember, Integer deletionDays);
CompletableFuture<BanResult> banUserWithNotification(User user, String reason, Member banningMember, Duration deletionDuration);
CompletableFuture<Void> unBanUserWithNotification(User user, Member unBanningUser);
CompletableFuture<Void> banUser(Guild guild, User user, Integer deletionDays, String reason);
CompletableFuture<Void> banUser(Guild guild, User user, Duration deletionDuration, String reason);
CompletableFuture<Void> unbanUser(Guild guild, User user);
CompletableFuture<Void> softBanUser(Guild guild, User user, Duration delDays);
}

View File

@@ -54,7 +54,7 @@
<properties>
<maven.build.timestamp.format>yyyy/MM/dd HH:mm</maven.build.timestamp.format>
<jda.version>5.0.0-beta.12</jda.version>
<jda.version>5.0.0-beta.13</jda.version>
<asciidoctor.maven.plugin.version>2.0.0-RC.1</asciidoctor.maven.plugin.version>
<asciidoctorj.pdf.version>1.5.3</asciidoctorj.pdf.version>
<asciidoctorj.version>2.3.0</asciidoctorj.version>