[AB-365] introducing slash commands for a selection of commands

adding method for pinning a message
moving suggestion to correct deployment
This commit is contained in:
Sheldan
2022-05-17 00:39:06 +02:00
parent 1913bc930d
commit 1d6de3f1e8
286 changed files with 8021 additions and 3065 deletions

View File

@@ -2,23 +2,29 @@ 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.config.*;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
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.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 dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.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.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -29,34 +35,107 @@ import static dev.sheldan.abstracto.moderation.service.BanService.BAN_EFFECT_KEY
@Slf4j
public class Ban extends AbstractConditionableCommand {
public static final String BAN_DEFAULT_REASON_TEMPLATE = "ban_default_reason";
private static final String BAN_COMMAND = "ban";
private static final String REASON_PARAMETER = "reason";
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";
@Autowired
private BanService banService;
@Autowired
private ChannelService channelService;
@Autowired
private TemplateService templateService;
@Autowired
private InteractionService interactionService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@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);
return banService.banUserWithNotification(user, reason, commandContext.getAuthor(), 0, commandContext.getMessage())
Guild guild = commandContext.getGuild();
Message message = commandContext.getMessage();
Member banningMember = commandContext.getAuthor();
return banService.banUserWithNotification(user, reason, commandContext.getAuthor(), 0)
.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(aVoid -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String reason = slashCommandParameterService.getCommandOption(REASON_PARAMETER, event, String.class, String.class);
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)
.thenCompose(banResult -> interactionService.replyEmbed(BAN_RESPONSE, event))
.thenApply(aVoid -> CommandResult.fromSuccess());
} else {
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(banResult -> interactionService.replyEmbed(BAN_RESPONSE, event))
.thenApply(banResult -> 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("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());
Parameter userParameter = Parameter
.builder()
.name(USER_PARAMETER)
.templated(true)
.type(User.class)
.build();
Parameter reasonParameter = Parameter
.builder()
.name(REASON_PARAMETER)
.templated(true)
.type(String.class)
.remainder(true)
.build();
List<Parameter> parameters = Arrays.asList(userParameter, reasonParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
EffectConfig banEffect = EffectConfig
.builder()
.position(0)
.parameterName(USER_PARAMETER)
.effectKey(BAN_EFFECT_KEY)
.build();
List<EffectConfig> effectConfig = Arrays.asList(banEffect);
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.MODERATION)
.commandName(BAN_COMMAND)
.build();
return CommandConfiguration.builder()
.name("ban")
.name(BAN_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.effects(effectConfig)
.supportsEmbedException(true)

View File

@@ -9,10 +9,15 @@ 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;
@@ -22,6 +27,7 @@ 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
@@ -31,13 +37,28 @@ 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);
return banService.banUserWithNotification(user, reason, commandContext.getAuthor(), delDays, commandContext.getMessage())
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());
}

View File

@@ -4,14 +4,18 @@ import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
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.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
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.WarnService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -22,12 +26,18 @@ import java.util.concurrent.CompletableFuture;
@Component
public class DecayAllWarnings extends AbstractConditionableCommand {
private static final String DECAY_ALL_WARNINGS_COMMAND = "decayAllWarnings";
private static final String DECAY_ALL_WARNINGS_RESPONSE = "decayAllWarnings_response";
@Autowired
private WarnService warnService;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
AServer server = serverManagementService.loadServer(commandContext.getGuild());
@@ -35,15 +45,35 @@ public class DecayAllWarnings extends AbstractConditionableCommand {
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
AServer server = serverManagementService.loadServer(event.getGuild());
return warnService.decayAllWarningsForServer(server)
.thenCompose(unused -> interactionService.replyEmbed(DECAY_ALL_WARNINGS_RESPONSE, event))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.MUTE)
.commandName(DECAY_ALL_WARNINGS_COMMAND)
.build();
return CommandConfiguration.builder()
.name("decayAllWarnings")
.name(DECAY_ALL_WARNINGS_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.requiresConfirmation(true)
.supportsEmbedException(true)
.causesReaction(true)

View File

@@ -4,14 +4,18 @@ import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
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.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
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.WarnService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -22,12 +26,18 @@ import java.util.concurrent.CompletableFuture;
@Component
public class DecayWarnings extends AbstractConditionableCommand {
private static final String DECAY_WARNINGS_COMMAND = "decayWarnings";
private static final String DECAY_WARNINGS_RESPONSE = "decayWarnings_response";
@Autowired
private WarnService warnService;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
AServer server = serverManagementService.loadServer(commandContext.getGuild());
@@ -35,14 +45,34 @@ public class DecayWarnings extends AbstractConditionableCommand {
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
AServer server = serverManagementService.loadServer(event.getGuild());
return warnService.decayWarningsForServer(server)
.thenCompose(unused -> interactionService.replyEmbed(DECAY_WARNINGS_RESPONSE, event))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.WARN_DECAY)
.commandName(DECAY_WARNINGS_COMMAND)
.build();
return CommandConfiguration.builder()
.name("decayWarnings")
.name(DECAY_WARNINGS_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.requiresConfirmation(true)
.async(true)
.supportsEmbedException(true)

View File

@@ -1,61 +1,87 @@
package dev.sheldan.abstracto.moderation.command;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.ParameterValidator;
import dev.sheldan.abstracto.core.command.config.*;
import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.interaction.InteractionService;
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.model.database.UserNote;
import dev.sheldan.abstracto.moderation.service.management.UserNoteManagementService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
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;
@Component
public class DeleteNote extends AbstractConditionableCommand {
public static final String NOTE_NOT_FOUND_EXCEPTION_TEMPLATE = "note_not_found_exception";
private static final String DELETE_NOTE_COMMAND = "deleteNote";
private static final String DELETE_NOTE_RESPONSE = "deleteNote_response";
private static final String ID_PARAMETER = "id";
@Autowired
private UserNoteManagementService userNoteManagementService;
@Autowired
private TemplateService templateService;
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private ServerManagementService serverManagementService;
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
Long id = (Long) commandContext.getParameters().getParameters().get(0);
AServer server = serverManagementService.loadServer(commandContext.getGuild());
if(userNoteManagementService.noteExists(id, server)) {
userNoteManagementService.deleteNote(id, server);
} else {
// TODO replace with exception
return CommandResult.fromError(templateService.renderSimpleTemplate(NOTE_NOT_FOUND_EXCEPTION_TEMPLATE, commandContext.getGuild().getIdLong()));
}
UserNote existingNote = userNoteManagementService.loadNote(commandContext.getGuild().getIdLong(), id);
userNoteManagementService.deleteNote(existingNote);
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long userNoteId = slashCommandParameterService.getCommandOption(ID_PARAMETER, event, Integer.class).longValue();
UserNote existingNote = userNoteManagementService.loadNote(event.getGuild().getIdLong(), userNoteId);
userNoteManagementService.deleteNote(existingNote);
return interactionService.replyEmbed(DELETE_NOTE_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
List<ParameterValidator> userNoteIdValidator = Arrays.asList(MinIntegerValueValidator.min(1L));
parameters.add(Parameter.builder().name("id").validators(userNoteIdValidator).type(Long.class).templated(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter idParameter = Parameter
.builder()
.name(ID_PARAMETER)
.validators(userNoteIdValidator)
.type(Long.class)
.templated(true)
.build();
parameters.add(idParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.USER_NOTES)
.commandName("delete")
.build();
return CommandConfiguration.builder()
.name("deleteNote")
.name(DELETE_NOTE_COMMAND)
.slashCommandConfig(slashCommandConfig)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.supportsEmbedException(true)

View File

@@ -1,49 +1,87 @@
package dev.sheldan.abstracto.moderation.command;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.ParameterValidator;
import dev.sheldan.abstracto.core.command.config.*;
import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
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.model.database.Warning;
import dev.sheldan.abstracto.moderation.service.management.WarnManagementService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
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.Optional;
import java.util.concurrent.CompletableFuture;
@Component
public class DeleteWarning extends AbstractConditionableCommand {
private static final String DELETE_WARNING_COMMAND = "deleteWarning";
private static final String DELETE_WARNING_RESPONSE = "deleteWarning_response";
private static final String WARN_ID_PARAMETER = "warnId";
@Autowired
private WarnManagementService warnManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
Long warnId = (Long) commandContext.getParameters().getParameters().get(0);
Optional<Warning> optional = warnManagementService.findByIdOptional(warnId, commandContext.getGuild().getIdLong());
optional.ifPresent(warning -> warnManagementService.deleteWarning(warning));
Warning warning = warnManagementService.findById(warnId, commandContext.getGuild().getIdLong());
warnManagementService.deleteWarning(warning);
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long warnId = slashCommandParameterService.getCommandOption(WARN_ID_PARAMETER, event, Long.class, Integer.class).longValue();
Warning warning = warnManagementService.findById(warnId, event.getGuild().getIdLong());
warnManagementService.deleteWarning(warning);
return interactionService.replyEmbed(DELETE_WARNING_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
List<ParameterValidator> warnIdValidator = Arrays.asList(MinIntegerValueValidator.min(1L));
parameters.add(Parameter.builder().name("warnId").validators(warnIdValidator).templated(true).type(Long.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter warnIdParameter = Parameter
.builder()
.name(WARN_ID_PARAMETER)
.validators(warnIdValidator)
.templated(true)
.type(Long.class)
.build();
List<Parameter> parameters = Arrays.asList(warnIdParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.WARNINGS)
.commandName("delete")
.build();
List<String> aliases = Arrays.asList("delWarn");
return CommandConfiguration.builder()
.name("deleteWarning")
.name(DELETE_WARNING_COMMAND)
.slashCommandConfig(slashCommandConfig)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.supportsEmbedException(true)

View File

@@ -2,25 +2,24 @@ 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.config.*;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
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.model.template.command.KickLogModel;
import dev.sheldan.abstracto.moderation.service.KickServiceBean;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
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;
@@ -31,12 +30,22 @@ import static dev.sheldan.abstracto.moderation.service.KickService.KICK_EFFECT_K
public class Kick extends AbstractConditionableCommand {
public static final String KICK_DEFAULT_REASON_TEMPLATE = "kick_default_reason";
public static final String KICK_COMMAND = "kick";
public static final String USER_PARAMETER = "user";
public static final String REASON_PARAMETER = "reason";
private static final String KICK_RESPONSE = "kick_response";
@Autowired
private TemplateService templateService;
@Autowired
private KickServiceBean kickService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
@@ -47,24 +56,90 @@ public class Kick extends AbstractConditionableCommand {
String defaultReason = templateService.renderSimpleTemplate(KICK_DEFAULT_REASON_TEMPLATE, commandContext.getGuild().getIdLong());
String reason = parameters.size() == 2 ? (String) parameters.get(1) : defaultReason;
KickLogModel kickLogModel = (KickLogModel) ContextConverter.slimFromCommandContext(commandContext, KickLogModel.class);
KickLogModel kickLogModel = KickLogModel
.builder()
.kickedUser(member)
.reason(reason)
.guild(commandContext.getGuild())
.channel(commandContext.getChannel())
.member(commandContext.getAuthor())
.build();
kickLogModel.setKickedUser(member);
kickLogModel.setReason(reason);
return kickService.kickMember(member, reason, kickLogModel)
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Member member = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, Member.class);
if(!member.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
String reason;
if(slashCommandParameterService.hasCommandOption(REASON_PARAMETER, event)) {
reason = slashCommandParameterService.getCommandOption(REASON_PARAMETER, event, String.class);
} else {
reason = templateService.renderSimpleTemplate(KICK_DEFAULT_REASON_TEMPLATE, event.getGuild().getIdLong());
}
KickLogModel kickLogModel = KickLogModel
.builder()
.kickedUser(member)
.reason(reason)
.guild(event.getGuild())
.channel(event.getGuildChannel())
.member(event.getMember())
.build();
kickLogModel.setKickedUser(member);
kickLogModel.setReason(reason);
return kickService.kickMember(member, reason, kickLogModel)
.thenCompose(unused -> interactionService.replyEmbed(KICK_RESPONSE, event))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").templated(true).type(Member.class).build());
parameters.add(Parameter.builder().name("reason").templated(true).type(String.class).optional(true).remainder(true).build());
List<EffectConfig> effectConfig = Arrays.asList(EffectConfig.builder().position(0).effectKey(KICK_EFFECT_KEY).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
Parameter userParameter = Parameter
.builder()
.name(USER_PARAMETER)
.templated(true)
.type(Member.class)
.build();
Parameter reasonParameter = Parameter
.builder()
.name(REASON_PARAMETER)
.templated(true)
.type(String.class)
.optional(true)
.remainder(true)
.build();
List<Parameter> parameters = Arrays.asList(userParameter, reasonParameter);
EffectConfig kickEffect = EffectConfig
.builder()
.position(0)
.parameterName(USER_PARAMETER)
.effectKey(KICK_EFFECT_KEY)
.build();
List<EffectConfig> effectConfig = Arrays.asList(kickEffect);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.MODERATION)
.commandName(KICK_COMMAND)
.build();
return CommandConfiguration.builder()
.name("kick")
.name(KICK_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.async(true)
.effects(effectConfig)

View File

@@ -2,27 +2,30 @@ 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.config.*;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
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.model.template.command.MuteContext;
import dev.sheldan.abstracto.moderation.service.MuteService;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -32,7 +35,12 @@ import static dev.sheldan.abstracto.moderation.service.MuteService.MUTE_EFFECT_K
@Component
public class Mute extends AbstractConditionableCommand {
public static final String MUTE_DEFAULT_REASON_TEMPLATE = "mute_default_reason";
private static final String MUTE_DEFAULT_REASON_TEMPLATE = "mute_default_reason";
private static final String DURATION_PARAMETER = "duration";
private static final String MUTE_COMMAND = "mute";
private static final String USER_PARAMETER = "user";
private static final String REASON_PARAMETER = "reason";
private static final String MUTE_RESPONSE = "mute_response";
@Autowired
private MuteService muteService;
@@ -40,20 +48,28 @@ public class Mute extends AbstractConditionableCommand {
@Autowired
private TemplateService templateService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Member member = (Member) parameters.get(0);
if(!member.getGuild().equals(commandContext.getGuild())) {
Guild guild = commandContext.getGuild();
GuildMessageChannel channel = commandContext.getChannel();
if(!member.getGuild().equals(guild)) {
throw new EntityGuildMismatchException();
}
Duration duration = (Duration) parameters.get(1);
String defaultReason = templateService.renderSimpleTemplate(MUTE_DEFAULT_REASON_TEMPLATE, commandContext.getGuild().getIdLong());
String defaultReason = templateService.renderSimpleTemplate(MUTE_DEFAULT_REASON_TEMPLATE, guild.getIdLong());
String reason = parameters.size() == 3 ? (String) parameters.get(2) : defaultReason;
ServerChannelMessage context = ServerChannelMessage
.builder()
.serverId(commandContext.getGuild().getIdLong())
.channelId(commandContext.getChannel().getIdLong())
.serverId(guild.getIdLong())
.channelId(channel.getIdLong())
.messageId(commandContext.getMessage().getIdLong())
.build();
MuteContext muteLogModel = MuteContext
@@ -62,7 +78,7 @@ public class Mute extends AbstractConditionableCommand {
.muteTargetDate(Instant.now().plus(duration))
.mutedUser(member)
.reason(reason)
.contextChannel(commandContext.getChannel())
.contextChannel(channel)
.message(commandContext.getMessage())
.mutingUser(commandContext.getAuthor())
.context(context)
@@ -71,19 +87,87 @@ public class Mute extends AbstractConditionableCommand {
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Guild guild = event.getGuild();
GuildMessageChannel channel = event.getGuildChannel();
Member targetMember = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, Member.class);
String durationStr = slashCommandParameterService.getCommandOption(DURATION_PARAMETER, event, Duration.class, String.class);
Duration duration = ParseUtils.parseDuration(durationStr);
String reason;
if(slashCommandParameterService.hasCommandOption(REASON_PARAMETER, event)) {
reason = slashCommandParameterService.getCommandOption(REASON_PARAMETER, event, String.class);
} else {
reason = templateService.renderSimpleTemplate(MUTE_DEFAULT_REASON_TEMPLATE, guild.getIdLong());
}
ServerChannelMessage context = ServerChannelMessage
.builder()
.serverId(guild.getIdLong())
.channelId(channel.getIdLong())
.build();
MuteContext muteLogModel = MuteContext
.builder()
.muteDate(Instant.now())
.muteTargetDate(Instant.now().plus(duration))
.mutedUser(targetMember)
.reason(reason)
.contextChannel(channel)
.mutingUser(event.getMember())
.context(context)
.build();
return muteService.muteMemberWithLog(muteLogModel)
.thenCompose(unused -> interactionService.replyEmbed(MUTE_RESPONSE, event))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").templated(true).type(Member.class).build());
parameters.add(Parameter.builder().name("duration").templated(true).type(Duration.class).build());
parameters.add(Parameter.builder().name("reason").templated(true).type(String.class).optional(true).remainder(true).build());
Parameter userParameter = Parameter
.builder()
.name(USER_PARAMETER)
.templated(true)
.type(Member.class)
.build();
Parameter durationParameter = Parameter
.builder()
.name(DURATION_PARAMETER)
.templated(true)
.type(Duration.class)
.build();
Parameter reasonParameter = Parameter
.builder()
.name(REASON_PARAMETER)
.templated(true)
.type(String.class)
.optional(true)
.remainder(true)
.build();
List<Parameter> parameters = Arrays.asList(userParameter, durationParameter, reasonParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
List<EffectConfig> effectConfig = Arrays.asList(EffectConfig.builder().position(0).effectKey(MUTE_EFFECT_KEY).build());
EffectConfig muteEffect = EffectConfig
.builder()
.position(0)
.parameterName(USER_PARAMETER)
.effectKey(MUTE_EFFECT_KEY)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.MUTE)
.commandName("create")
.build();
List<EffectConfig> effectConfig = Arrays.asList(muteEffect);
return CommandConfiguration.builder()
.name("mute")
.name(MUTE_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.effects(effectConfig)
.causesReaction(true)
.supportsEmbedException(true)

View File

@@ -4,11 +4,13 @@ import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.PaginatorService;
@@ -18,12 +20,14 @@ import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
import dev.sheldan.abstracto.moderation.config.ModerationSlashCommandNames;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.converter.MuteEntryConverter;
import dev.sheldan.abstracto.moderation.model.template.command.MuteEntry;
import dev.sheldan.abstracto.moderation.model.template.command.MutesModel;
import dev.sheldan.abstracto.moderation.service.management.MuteManagementService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -37,6 +41,8 @@ public class Mutes extends AbstractConditionableCommand {
private static final String NO_MUTES_TEMPLATE_KEY = "mutes_no_mutes_found";
private static final String MUTES_DISPLAY_TEMPLATE_KEY = "mutes_display_response";
public static final String MUTES_COMMAND = "mutes";
public static final String MEMBER_PARAMETER = "member";
@Autowired
private MuteManagementService muteManagementService;
@@ -61,6 +67,12 @@ public class Mutes extends AbstractConditionableCommand {
@Autowired
private PaginatorService paginatorService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<dev.sheldan.abstracto.moderation.model.database.Mute> mutesToDisplay;
@@ -87,20 +99,74 @@ public class Mutes extends AbstractConditionableCommand {
@Transactional
public CompletableFuture<Void> renderMutes(CommandContext commandContext, List<MuteEntry> mutes) {
MutesModel model = (MutesModel) ContextConverter.slimFromCommandContext(commandContext, MutesModel.class);
model.setMutes(mutes);
MutesModel model = MutesModel
.builder()
.mutes(mutes)
.build();
return paginatorService.createPaginatorFromTemplate(MUTES_DISPLAY_TEMPLATE_KEY, model, commandContext.getChannel(), commandContext.getAuthor().getIdLong());
}
@Transactional
public CompletableFuture<Void> renderMutes(SlashCommandInteractionEvent event, List<MuteEntry> mutes) {
MutesModel model = MutesModel
.builder()
.mutes(mutes)
.build();
return paginatorService.createPaginatorFromTemplate(MUTES_DISPLAY_TEMPLATE_KEY, model, event);
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
List<dev.sheldan.abstracto.moderation.model.database.Mute> mutesToDisplay;
if(!slashCommandParameterService.hasCommandOption(MEMBER_PARAMETER, event)) {
AServer server = serverManagementService.loadServer(event.getGuild().getIdLong());
mutesToDisplay = muteManagementService.getAllMutes(server);
} else {
Member memberParameter = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER, event, Member.class, Member.class);
if(!memberParameter.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
mutesToDisplay = muteManagementService.getAllMutesOf(userInServerManagementService.loadOrCreateUser(memberParameter));
}
if(mutesToDisplay.isEmpty()) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(NO_MUTES_TEMPLATE_KEY, new Object(), event.getGuild().getIdLong());
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(unused -> CommandResult.fromSuccess());
} else {
return muteEntryConverter.fromMutes(mutesToDisplay)
.thenCompose(muteEntries -> self.renderMutes(event, muteEntries)
.thenApply(unused -> CommandResult.fromIgnored()));
}
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("member").templated(true).type(Member.class).optional(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter memberParameter = Parameter
.builder()
.name(MEMBER_PARAMETER)
.templated(true)
.type(Member.class)
.optional(true)
.build();
parameters.add(memberParameter);
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.MUTE)
.commandName("list")
.build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("mutes")
.name(MUTES_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.async(true)
.causesReaction(true)

View File

@@ -4,28 +4,33 @@ import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
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.model.template.command.MyWarningsModel;
import dev.sheldan.abstracto.moderation.service.management.WarnManagementService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
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;
@Component
public class MyWarnings extends AbstractConditionableCommand {
public static final String MY_WARNINGS_RESPONSE_EMBED_TEMPLATE = "myWarnings_response";
private static final String MY_WARNINGS_COMMAND = "myWarnings";
@Autowired
private ChannelService channelService;
@@ -35,27 +40,57 @@ public class MyWarnings extends AbstractConditionableCommand {
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
MyWarningsModel model = (MyWarningsModel) ContextConverter.fromCommandContext(commandContext, MyWarningsModel.class);
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
Long currentWarnCount = warnManagementService.getActiveWarnCountForUser(userInAServer);
model.setCurrentWarnCount(currentWarnCount);
Long totalWarnCount = warnManagementService.getTotalWarnsForUser(userInAServer);
model.setTotalWarnCount(totalWarnCount);
MyWarningsModel model = MyWarningsModel
.builder()
.member(commandContext.getAuthor())
.totalWarnCount(totalWarnCount)
.currentWarnCount(currentWarnCount)
.build();
channelService.sendEmbedTemplateInTextChannelList(MY_WARNINGS_RESPONSE_EMBED_TEMPLATE, model, commandContext.getChannel());
return CommandResult.fromIgnored();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(event.getMember());
Long currentWarnCount = warnManagementService.getActiveWarnCountForUser(userInAServer);
Long totalWarnCount = warnManagementService.getTotalWarnsForUser(userInAServer);
MyWarningsModel model = MyWarningsModel
.builder()
.member(event.getMember())
.totalWarnCount(totalWarnCount)
.currentWarnCount(currentWarnCount)
.build();
return interactionService.replyEmbed(MY_WARNINGS_RESPONSE_EMBED_TEMPLATE, model, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
List<String> aliases = Arrays.asList("myWarns");
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.WARNINGS_PUBLIC)
.commandName(MY_WARNINGS_COMMAND)
.build();
return CommandConfiguration.builder()
.name("myWarnings")
.name(MY_WARNINGS_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(true)
.aliases(aliases)

View File

@@ -1,19 +1,21 @@
package dev.sheldan.abstracto.moderation.command;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.ParameterValidator;
import dev.sheldan.abstracto.core.command.config.*;
import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.utils.SnowflakeUtils;
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.PurgeService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -25,9 +27,20 @@ import java.util.concurrent.CompletableFuture;
@Component
public class Purge extends AbstractConditionableCommand {
private static final String AMOUNT_PARAMETER = "amount";
private static final String MEMBER_PARAMETER = "member";
private static final String PURGE_COMMAND = "purge";
private static final String PURGE_INITIAL_MESSAGE = "purge_initial_message";
@Autowired
private PurgeService purgeService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
Integer amountOfMessages = (Integer) commandContext.getParameters().getParameters().get(0);
@@ -42,17 +55,62 @@ public class Purge extends AbstractConditionableCommand {
.thenApply(aVoid -> CommandResult.fromSelfDestruct());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Integer amountOfMessages = slashCommandParameterService.getCommandOption(AMOUNT_PARAMETER, event, Integer.class);
Member memberToPurgeMessagesOf;
if(slashCommandParameterService.hasCommandOption(MEMBER_PARAMETER, event)) {
memberToPurgeMessagesOf = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER, event, Member.class);
if(!memberToPurgeMessagesOf.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
} else {
memberToPurgeMessagesOf = null;
}
return interactionService.replyEmbed(PURGE_INITIAL_MESSAGE, event)
.thenCompose(interactionHook -> {
Long startMessageId = SnowflakeUtils.createSnowFlake();
return purgeService.purgeMessagesInChannel(amountOfMessages, event.getGuildChannel(), startMessageId , event.getHook(), memberToPurgeMessagesOf);
}).thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
List<ParameterValidator> messageAmountValidator = Arrays.asList(MinIntegerValueValidator.min(1L));
parameters.add(Parameter.builder().name("amount").validators(messageAmountValidator).type(Integer.class).templated(true).build());
parameters.add(Parameter.builder().name("member").type(Member.class).optional(true).templated(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter amountParameter = Parameter
.builder()
.name(AMOUNT_PARAMETER)
.validators(messageAmountValidator)
.type(Integer.class)
.templated(true)
.build();
parameters.add(amountParameter);
Parameter memberParameter = Parameter
.builder()
.name(MEMBER_PARAMETER)
.type(Member.class)
.optional(true)
.templated(true)
.build();
parameters.add(memberParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.MODERATION)
.commandName(PURGE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("purge")
.name(PURGE_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)

View File

@@ -4,29 +4,45 @@ import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
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.SlowModeService;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
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;
@Component
public class SlowMode extends AbstractConditionableCommand {
private static final String CHANNEL_PARAMETER = "channel";
private static final String DURATION_PARAMETER = "duration";
private static final String SLOWMODE_COMMAND = "slowmode";
private static final String SLOWMODE_RESPONSE = "slowmode_response";
@Autowired
private SlowModeService slowModeService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
TextChannel channel;
@@ -53,16 +69,68 @@ public class SlowMode extends AbstractConditionableCommand {
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
TextChannel channel;
String durationString = slashCommandParameterService.getCommandOption(DURATION_PARAMETER, event, String.class);
Duration duration;
if(durationString.equalsIgnoreCase("off")) {
duration = Duration.ZERO;
} else {
duration = ParseUtils.parseDuration(durationString);
}
if(slashCommandParameterService.hasCommandOption(CHANNEL_PARAMETER, event)) {
channel = slashCommandParameterService.getCommandOption(CHANNEL_PARAMETER, event, TextChannel.class);
if(!channel.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
} else {
if(event.getChannel() instanceof TextChannel) {
channel = (TextChannel) event.getChannel();
} else {
throw new IllegalArgumentException("Not a text channel.");
}
}
return slowModeService.setSlowMode(channel, duration)
.thenCompose(unused -> interactionService.replyEmbed(SLOWMODE_RESPONSE, event))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("duration").type(String.class).templated(true).build());
parameters.add(Parameter.builder().name("channel").type(TextChannel.class).templated(true).optional(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
Parameter durationParameter = Parameter
.builder()
.name(DURATION_PARAMETER)
.type(String.class)
.templated(true)
.build();
Parameter channelParameter = Parameter
.builder()
.name(CHANNEL_PARAMETER)
.type(TextChannel.class)
.templated(true)
.optional(true)
.build();
List<Parameter> parameters = Arrays.asList(durationParameter, channelParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.MODERATION)
.commandName(SLOWMODE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("slowmode")
.name(SLOWMODE_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)

View File

@@ -5,15 +5,19 @@ import dev.sheldan.abstracto.core.command.condition.CommandCondition;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.UserService;
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.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -24,30 +28,70 @@ import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class UnBan extends AbstractConditionableCommand {
private static final String USER_PARAMETER = "user";
private static final String UN_BAN_COMMAND = "unBan";
private static final String UN_BAN_RESPONSE = "unBan_response";
@Autowired
private BanService banService;
@Autowired
private TemplateService templateService;
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private UserService userService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
User user = (User) parameters.get(0);
return banService.unBanUserWithNotification(user, commandContext.getAuthor())
String userIdStr = (String) parameters.get(0);
Long userId = Long.parseLong(userIdStr);
return userService.retrieveUserForId(userId)
.thenCompose(user -> banService.unBanUserWithNotification(user, commandContext.getAuthor()))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String userIdStr = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, String.class);
Long userId = Long.parseLong(userIdStr);
return userService.retrieveUserForId(userId)
.thenCompose(user -> banService.unBanUserWithNotification(user, event.getMember()))
.thenCompose(unused -> interactionService.replyEmbed(UN_BAN_RESPONSE, event))
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").templated(true).type(User.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter userParameter = Parameter
.builder()
.name(USER_PARAMETER)
.templated(true)
.type(String.class)
.build();
parameters.add(userParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.MODERATION)
.commandName(UN_BAN_COMMAND)
.build();
return CommandConfiguration.builder()
.name("unBan")
.name(UN_BAN_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -4,32 +4,43 @@ import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
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.MuteService;
import dev.sheldan.abstracto.moderation.service.management.MuteManagementService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
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;
@Component
public class UnMute extends AbstractConditionableCommand {
private static final String UN_MUTE_COMMAND = "unMute";
private static final String USER_PARAMETER = "user";
private static final String UN_MUTE_RESPONSE = "unMute_response";
@Autowired
private MuteService muteService;
@Autowired
private MuteManagementService muteManagementService;
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@@ -47,16 +58,45 @@ public class UnMute extends AbstractConditionableCommand {
);
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Member targetMember = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, Member.class);
if(!targetMember.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
AUserInAServer userToUnMute = userInServerManagementService.loadOrCreateUser(targetMember);
return muteService.unMuteUser(userToUnMute)
.thenCompose(unused -> interactionService.replyEmbed(UN_MUTE_RESPONSE, event))
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").type(Member.class).templated(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter userParameter = Parameter
.builder()
.name(USER_PARAMETER)
.type(Member.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(userParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.MUTE)
.commandName("remove")
.build();
return CommandConfiguration.builder()
.name("unMute")
.name(UN_MUTE_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -4,31 +4,48 @@ import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
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.management.UserNoteManagementService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
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;
@Component
public class UserNoteCommand extends AbstractConditionableCommand {
private static final String USER_PARAMETER = "user";
private static final String TEXT_PARAMETER = "text";
private static final String USER_NOTE_COMMAND = "userNote";
private static final String USER_NOTE_RESPONSE = "userNote_response";
@Autowired
private UserNoteManagementService userNoteManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
@@ -42,14 +59,50 @@ public class UserNoteCommand extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Member member = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, Member.class);
if(!member.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
String text = slashCommandParameterService.getCommandOption(TEXT_PARAMETER, event, String.class);
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(member);
userNoteManagementService.createUserNote(userInAServer, text);
return interactionService.replyEmbed(USER_NOTE_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").type(Member.class).templated(true).build());
parameters.add(Parameter.builder().name("text").type(String.class).templated(true).remainder(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter userParameter = Parameter
.builder()
.name(USER_PARAMETER)
.type(Member.class)
.templated(true)
.build();
Parameter textParameter = Parameter
.builder()
.name(TEXT_PARAMETER)
.type(String.class)
.templated(true)
.remainder(true)
.build();
List<Parameter> parameters = Arrays.asList(userParameter, textParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.USER_NOTES)
.commandName("create")
.build();
return CommandConfiguration.builder()
.name("userNote")
.name(USER_NOTE_COMMAND)
.slashCommandConfig(slashCommandConfig)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.supportsEmbedException(true)

View File

@@ -4,11 +4,13 @@ import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
@@ -17,6 +19,7 @@ import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
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.converter.UserNotesConverter;
import dev.sheldan.abstracto.moderation.model.database.UserNote;
@@ -24,6 +27,7 @@ import dev.sheldan.abstracto.moderation.model.template.command.ListNotesModel;
import dev.sheldan.abstracto.moderation.model.template.command.NoteEntryModel;
import dev.sheldan.abstracto.moderation.service.management.UserNoteManagementService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -34,6 +38,8 @@ import java.util.concurrent.CompletableFuture;
@Component
public class UserNotes extends AbstractConditionableCommand {
public static final String USER_NOTES_RESPONSE_TEMPLATE = "user_notes_response";
public static final String USER_NOTES_COMMAND = "userNotes";
public static final String USER_PARAMETER = "user";
@Autowired
private UserNoteManagementService userNoteManagementService;
@@ -49,12 +55,21 @@ public class UserNotes extends AbstractConditionableCommand {
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
List<UserNote> userNotes;
ListNotesModel model = (ListNotesModel) ContextConverter.fromCommandContext(commandContext, ListNotesModel.class);
ListNotesModel model = ListNotesModel
.builder()
.member(commandContext.getAuthor())
.build();
if(parameters.size() == 1) {
Member member = (Member) parameters.get(0);
if(!member.getGuild().equals(commandContext.getGuild())) {
@@ -80,17 +95,68 @@ public class UserNotes extends AbstractConditionableCommand {
});
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
List<UserNote> userNotes;
ListNotesModel model = ListNotesModel
.builder()
.member(event.getMember())
.build();
if(slashCommandParameterService.hasCommandOption(USER_PARAMETER, event)) {
Member member = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, Member.class);
if(!member.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(member);
userNotes = userNoteManagementService.loadNotesForUser(userInAServer);
FullUserInServer specifiedUser = FullUserInServer
.builder()
.aUserInAServer(userInAServer)
.member(member)
.build();
model.setSpecifiedUser(specifiedUser);
} else {
AServer server = serverManagementService.loadServer(event.getGuild());
userNotes = userNoteManagementService.loadNotesForServer(server);
}
CompletableFuture<List<NoteEntryModel>> listCompletableFuture = userNotesConverter.fromNotes(userNotes);
return listCompletableFuture.thenCompose(noteEntryModels -> {
model.setUserNotes(noteEntryModels);
return interactionService.replyEmbed(USER_NOTES_RESPONSE_TEMPLATE, model, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
});
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
Parameter user = Parameter.builder().name("user").type(Member.class).optional(true).templated(true).build();
Parameter user = Parameter
.builder()
.name(USER_PARAMETER)
.type(Member.class)
.optional(true)
.templated(true)
.build();
parameters.add(user);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.USER_NOTES)
.commandName("list")
.build();
return CommandConfiguration.builder()
.name("userNotes")
.name(USER_NOTES_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -2,26 +2,25 @@ 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.config.*;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
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.model.template.command.WarnContext;
import dev.sheldan.abstracto.moderation.service.WarnService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
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;
@@ -33,12 +32,23 @@ import static dev.sheldan.abstracto.moderation.service.WarnService.WARN_EFFECT_K
public class Warn extends AbstractConditionableCommand {
public static final String WARN_DEFAULT_REASON_TEMPLATE = "warn_default_reason";
private static final String WARN_COMMAND = "warn";
private static final String USER_PARAMETER = "user";
private static final String REASON_PARAMETER = "reason";
private static final String WARN_RESPONSE = "warn_response";
@Autowired
private WarnService warnService;
@Autowired
private TemplateService templateService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
@@ -48,25 +58,87 @@ public class Warn extends AbstractConditionableCommand {
}
String defaultReason = templateService.renderSimpleTemplate(WARN_DEFAULT_REASON_TEMPLATE, commandContext.getGuild().getIdLong());
String reason = parameters.size() == 2 ? (String) parameters.get(1) : defaultReason;
WarnContext warnLogModel = (WarnContext) ContextConverter.slimFromCommandContext(commandContext, WarnContext.class);
warnLogModel.setReason(reason);
warnLogModel.setWarnedMember(member);
WarnContext warnLogModel = WarnContext
.builder()
.reason(reason)
.warnedMember(member)
.member(commandContext.getAuthor())
.guild(commandContext.getGuild())
.message(commandContext.getMessage())
.build();
return warnService.warnUserWithLog(warnLogModel)
.thenApply(warning -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Member member = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, Member.class);
if(!member.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
String reason;
if(slashCommandParameterService.hasCommandOption(REASON_PARAMETER, event)) {
reason = slashCommandParameterService.getCommandOption(REASON_PARAMETER, event, String.class);
} else {
reason = templateService.renderSimpleTemplate(WARN_DEFAULT_REASON_TEMPLATE, event.getGuild().getIdLong());
}
WarnContext warnLogModel = WarnContext
.builder()
.reason(reason)
.warnedMember(member)
.member(event.getMember())
.channel(event.getGuildChannel())
.guild(event.getGuild())
.build();
return warnService.warnUserWithLog(warnLogModel)
.thenCompose(unused -> interactionService.replyEmbed(WARN_RESPONSE, event))
.thenApply(warning -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").type(Member.class).templated(true).build());
parameters.add(Parameter.builder().name("reason").type(String.class).templated(true).optional(true).remainder(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
List<EffectConfig> effectConfig = Arrays.asList(EffectConfig.builder().position(0).effectKey(WARN_EFFECT_KEY).build());
Parameter userParameter = Parameter
.builder()
.name(USER_PARAMETER)
.type(Member.class)
.templated(true)
.build();
Parameter reasonParameter = Parameter
.builder()
.name(REASON_PARAMETER)
.type(String.class)
.templated(true)
.optional(true)
.remainder(true)
.build();
HelpInfo helpInfo = HelpInfo
.builder().
templated(true)
.hasExample(true)
.build();
List<Parameter> parameters = Arrays.asList(userParameter, reasonParameter);
EffectConfig warnEffect = EffectConfig
.builder()
.position(0)
.parameterName(USER_PARAMETER)
.effectKey(WARN_EFFECT_KEY)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.WARNINGS)
.commandName("create")
.build();
List<EffectConfig> effectConfig = Arrays.asList(warnEffect);
return CommandConfiguration.builder()
.name("warn")
.name(WARN_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(true)
.effects(effectConfig)

View File

@@ -4,11 +4,13 @@ import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.PaginatorService;
@@ -18,6 +20,7 @@ import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
import dev.sheldan.abstracto.moderation.config.ModerationSlashCommandNames;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.converter.WarnEntryConverter;
import dev.sheldan.abstracto.moderation.model.database.Warning;
@@ -25,6 +28,7 @@ import dev.sheldan.abstracto.moderation.model.template.command.WarnEntry;
import dev.sheldan.abstracto.moderation.model.template.command.WarningsModel;
import dev.sheldan.abstracto.moderation.service.management.WarnManagementService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -38,6 +42,8 @@ public class Warnings extends AbstractConditionableCommand {
public static final String WARNINGS_RESPONSE_TEMPLATE = "warnings_display_response";
public static final String NO_WARNINGS_TEMPLATE_KEY = "warnings_no_warnings_found";
public static final String WARNINGS_COMMAND = "warnings";
public static final String USER_PARAMETER = "user";
@Autowired
private WarnManagementService warnManagementService;
@@ -62,6 +68,12 @@ public class Warnings extends AbstractConditionableCommand {
@Autowired
private ChannelService channelService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Warning> warnsToDisplay;
@@ -85,29 +97,83 @@ public class Warnings extends AbstractConditionableCommand {
.thenCompose(warnEntries -> self.renderWarnings(commandContext, warnEntries))
.thenApply(unused -> CommandResult.fromIgnored());
}
}
@Transactional
public CompletableFuture<Void> renderWarnings(CommandContext commandContext, List<WarnEntry> warnEntries) {
WarningsModel model = (WarningsModel) ContextConverter.slimFromCommandContext(commandContext, WarningsModel.class);
model.setWarnings(warnEntries);
WarningsModel model = WarningsModel
.builder()
.warnings(warnEntries)
.build();
return paginatorService.createPaginatorFromTemplate(WARNINGS_RESPONSE_TEMPLATE, model, commandContext.getChannel(), commandContext.getAuthor().getIdLong());
}
@Transactional
public CompletableFuture<Void> renderWarnings(SlashCommandInteractionEvent event, List<WarnEntry> warnEntries) {
WarningsModel model = WarningsModel
.builder()
.warnings(warnEntries)
.build();
return paginatorService.createPaginatorFromTemplate(WARNINGS_RESPONSE_TEMPLATE, model, event);
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
List<Warning> warnsToDisplay;
if(slashCommandParameterService.hasCommandOption(USER_PARAMETER, event)) {
Member member = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, Member.class);
if(!member.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
warnsToDisplay = warnManagementService.getAllWarnsForUser(userInServerManagementService.loadOrCreateUser(member));
} else {
AServer server = serverManagementService.loadServer(event.getGuild());
warnsToDisplay = warnManagementService.getAllWarningsOfServer(server);
}
if(warnsToDisplay.isEmpty()) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(NO_WARNINGS_TEMPLATE_KEY, new Object(), event.getGuild().getIdLong());
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
} else {
return warnEntryConverter.fromWarnings(warnsToDisplay)
.thenCompose(warnEntries -> self.renderWarnings(event, warnEntries))
.thenApply(unused -> CommandResult.fromIgnored());
}
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").type(Member.class).templated(true).optional(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter userParameter = Parameter
.builder()
.name(USER_PARAMETER)
.type(Member.class)
.templated(true)
.optional(true)
.build();
parameters.add(userParameter);
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModerationSlashCommandNames.WARNINGS)
.commandName("list")
.build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("warnings")
.name(WARNINGS_COMMAND)
.module(ModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.causesReaction(false)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -8,6 +8,7 @@ import org.springframework.lang.NonNull;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface UserNoteRepository extends JpaRepository<UserNote, Long> {
@@ -19,6 +20,6 @@ public interface UserNoteRepository extends JpaRepository<UserNote, Long> {
void deleteByUserNoteId_IdAndUserNoteId_ServerId(@NonNull Long aLong, Long serverId);
UserNote findByUserNoteId_IdAndUserNoteId_ServerId(Long userNoteId, Long serverId);
Optional<UserNote> findByUserNoteId_IdAndUserNoteId_ServerId(Long userNoteId, Long serverId);
}

View File

@@ -5,14 +5,12 @@ import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.config.posttarget.ModerationPostTarget;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.moderation.model.BanResult;
import dev.sheldan.abstracto.moderation.model.template.command.BanLog;
import dev.sheldan.abstracto.moderation.model.template.command.BanNotificationModel;
import dev.sheldan.abstracto.moderation.model.template.command.UnBanLog;
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.entities.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -26,10 +24,6 @@ public class BanServiceBean implements BanService {
public static final String BAN_LOG_TEMPLATE = "ban_log";
public static final String UN_BAN_LOG_TEMPLATE = "unBan_log";
public static final String BAN_NOTIFICATION = "ban_notification";
public static final String BAN_NOTIFICATION_NOT_POSSIBLE = "ban_notification_not_possible";
@Autowired
private GuildService guildService;
@Autowired
private TemplateService templateService;
@@ -37,63 +31,33 @@ public class BanServiceBean implements BanService {
@Autowired
private PostTargetService postTargetService;
@Autowired
private FeatureModeService featureModeService;
@Autowired
private BanServiceBean self;
@Autowired
private MessageService messageService;
@Autowired
private ChannelService channelService;
@Override
public CompletableFuture<Void> banMemberWithNotification(Member member, String reason, Member banningMember, Integer deletionDays, Message message) {
BanLog banLog = BanLog
.builder()
.bannedUser(member.getUser())
.banningMember(banningMember)
.commandMessage(message)
.reason(reason)
.build();
CompletableFuture<Void> banFuture = banUser(member.getGuild(), member.getUser(), deletionDays, reason);
CompletableFuture<Void> messageFuture = sendBanLogMessage(banLog, member.getGuild().getIdLong(), BAN_LOG_TEMPLATE);
return CompletableFuture.allOf(banFuture, messageFuture);
}
@Override
public CompletableFuture<Void> banUserWithNotification(User user, String reason, Member banningMember, Integer deletionDays, Message message) {
public CompletableFuture<BanResult> banUserWithNotification(User user, String reason, Member banningMember, Integer deletionDays) {
BanLog banLog = BanLog
.builder()
.bannedUser(user)
.banningMember(banningMember)
.commandMessage(message)
.deletionDays(deletionDays)
.reason(reason)
.build();
Guild guild = banningMember.getGuild();
CompletableFuture<Void> returningFuture = new CompletableFuture<>();
sendBanNotification(user, reason, message.getGuild()).whenComplete((unused, throwable) -> {
if(throwable != null) {
String errorNotification = templateService.renderSimpleTemplate(BAN_NOTIFICATION_NOT_POSSIBLE, guild.getIdLong());
channelService.sendTextToChannel(errorNotification, message.getChannel())
.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, deletionDays, reason);
CompletableFuture<Void> messageFuture = sendBanLogMessage(banLog, guild.getIdLong(), BAN_LOG_TEMPLATE);
CompletableFuture.allOf(banFuture, messageFuture)
.thenAccept(unused1 -> returningFuture.complete(null))
.exceptionally(throwable1 -> {
returningFuture.completeExceptionally(throwable1);
return null;
});
}).exceptionally(throwable -> {
returningFuture.completeExceptionally(throwable);
return null;
});
CompletableFuture<BanResult> returningFuture = new CompletableFuture<>();
sendBanNotification(user, reason, guild)
.whenComplete((unused, throwable) -> banUser(guild, user, deletionDays, reason)
.thenCompose(unused1 -> sendBanLogMessage(banLog, guild.getIdLong()))
.thenAccept(unused1 -> {
if(throwable != null) {
returningFuture.complete(BanResult.NOTIFICATION_FAILED);
} else {
returningFuture.complete(BanResult.SUCCESSFUL);
}
}));
return returningFuture;
}
@@ -138,8 +102,8 @@ public class BanServiceBean implements BanService {
.thenCompose(unused -> unbanUser(guild, user));
}
public CompletableFuture<Void> sendBanLogMessage(BanLog banLog, Long guildId, String template) {
MessageToSend banLogMessage = templateService.renderEmbedTemplate(template, banLog, guildId);
public CompletableFuture<Void> sendBanLogMessage(BanLog banLog, Long guildId) {
MessageToSend banLogMessage = templateService.renderEmbedTemplate(BAN_LOG_TEMPLATE, banLog, guildId);
log.debug("Sending ban log message in guild {}.", guildId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(banLogMessage, ModerationPostTarget.BAN_LOG, guildId));
}

View File

@@ -27,9 +27,6 @@ public class KickServiceBean implements KickService {
@Autowired
private PostTargetService postTargetService;
@Autowired
private FeatureModeService featureModeService;
@Override
public CompletableFuture<Void> kickMember(Member member, String reason, KickLogModel kickLogModel) {
Guild guild = member.getGuild();

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.service.ChannelService;
@@ -10,6 +11,8 @@ import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.utils.MiscUtil;
import net.dv8tion.jda.api.utils.TimeUtil;
import org.springframework.beans.factory.annotation.Autowired;
@@ -19,6 +22,7 @@ import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -39,6 +43,9 @@ public class PurgeServiceBean implements PurgeService {
@Autowired
private ChannelService channelService;
@Autowired
private InteractionService interactionService;
public static final String MODERATION_PURGE_METRIC = "moderation.purge";
private static final CounterMetric PURGE_MESSAGE_COUNTER_METRIC =
@@ -119,9 +126,9 @@ public class PurgeServiceBean implements PurgeService {
private CompletableFuture<Long> getOrCreatedStatusMessage(GuildMessageChannel channel, Integer totalCount, Long statusMessageId) {
CompletableFuture<Long> statusMessageFuture;
if(statusMessageId == 0) {
Long serverId = channel.getGuild().getIdLong();
log.debug("Creating new status message in channel {} in server {} because of puring.", channel.getIdLong(), channel.getGuild().getId());
PurgeStatusUpdateModel model = PurgeStatusUpdateModel.builder().currentlyDeleted(0).totalToDelete(totalCount).build();
MessageToSend messageToSend = templateService.renderTemplateToMessageToSend("purge_status_update", model, channel.getGuild().getIdLong());
MessageToSend messageToSend = getStatusMessageToSend(totalCount, serverId, 0);
statusMessageFuture = messageService.createStatusMessageId(messageToSend, channel);
} else {
log.debug("Using existing status message {}.", statusMessageId);
@@ -130,6 +137,15 @@ public class PurgeServiceBean implements PurgeService {
return statusMessageFuture;
}
private MessageToSend getStatusMessageToSend(Integer totalCount, Long serverId, Integer currentlyDeleted) {
PurgeStatusUpdateModel model = PurgeStatusUpdateModel
.builder()
.currentlyDeleted(currentlyDeleted)
.totalToDelete(totalCount)
.build();
return templateService.renderTemplateToMessageToSend("purge_status_update", model, serverId);
}
private List<Message> filterMessagesToDelete(List<Message> retrievedHistory, Member purgedMember) {
long twoWeeksAgo = TimeUtil.getDiscordTimestamp((System.currentTimeMillis() - (14 * 24 * 60 * 60 * 1000)));
log.debug("Filtering messages older than {}.", twoWeeksAgo);
@@ -174,17 +190,96 @@ public class PurgeServiceBean implements PurgeService {
deletionFuture.complete(null);
}
log.debug("Setting status for {} out of {}", currentCount, totalCount);
PurgeStatusUpdateModel finalUpdateModel = PurgeStatusUpdateModel.builder().currentlyDeleted(currentCount).totalToDelete(totalCount).build();
MessageToSend finalUpdateMessage = templateService.renderTemplateToMessageToSend("purge_status_update", finalUpdateModel);
MessageToSend finalUpdateMessage = getStatusMessageToSend(totalCount, channel.getGuild().getIdLong(), currentCount);
messageService.updateStatusMessage(channel, currentStatusMessageId, finalUpdateMessage);
};
}
private Consumer<Void> deletionConsumer(Integer amountToDelete, GuildMessageChannel channel, Member purgedMember, Integer totalCount, Integer currentCount, CompletableFuture<Void> deletionFuture, InteractionHook interactionHook, Message earliestMessage) {
return aVoid -> {
if (amountToDelete >= 1) {
log.debug("Still more than 1 message to delete. Continuing.");
purgeMessages(amountToDelete, channel, earliestMessage.getIdLong(), purgedMember, totalCount, currentCount, interactionHook)
.whenComplete((avoid, throwable) -> {
if (throwable != null) {
deletionFuture.completeExceptionally(throwable);
} else {
deletionFuture.complete(null);
}
}
).exceptionally(throwable -> {
deletionFuture.completeExceptionally(throwable);
return null;
});
} else {
log.debug("Completed purging of {} messages.", totalCount);
deletionFuture.complete(null);
}
log.debug("Setting status for {} out of {}", currentCount, totalCount);
MessageToSend finalUpdateMessage = getStatusMessageToSend(totalCount, channel.getGuild().getIdLong(), currentCount);
interactionService.editOriginal(finalUpdateMessage, interactionHook);
};
}
@Override
public CompletableFuture<Void> purgeMessagesInChannel(Integer count, GuildMessageChannel channel, Message origin, Member purgingRestriction) {
return purgeMessagesInChannel(count, channel, origin.getIdLong(), purgingRestriction);
}
@Override
public CompletionStage<Void> purgeMessagesInChannel(Integer amountOfMessages, GuildMessageChannel guildMessageChannel, Long startId, InteractionHook hook, Member memberToPurgeMessagesOf) {
return purgeMessages(amountOfMessages, guildMessageChannel, startId, memberToPurgeMessagesOf, amountOfMessages, 0, hook);
}
private CompletableFuture<Void> purgeMessages(Integer amountToDelete, GuildMessageChannel channel, Long startId, Member purgedMember, Integer totalCount, Integer currentCount, InteractionHook interactionHook) {
int toDeleteInThisIteration;
if(amountToDelete >= PURGE_MAX_MESSAGES){
toDeleteInThisIteration = PURGE_MAX_MESSAGES;
} else {
toDeleteInThisIteration = amountToDelete % PURGE_MAX_MESSAGES;
}
metricService.incrementCounter(PURGE_MESSAGE_COUNTER_METRIC, (long) toDeleteInThisIteration);
log.info("Purging {} this iteration ({}/{}) messages in channel {} in server {}.", toDeleteInThisIteration, currentCount, totalCount, channel.getId(), channel.getGuild().getId());
CompletableFuture<MessageHistory> historyFuture = channelService.getHistoryOfChannel(channel, startId, toDeleteInThisIteration);
MessageToSend statusMessageToSend = getStatusMessageToSend(totalCount, channel.getGuild().getIdLong(), 0);
CompletableFuture<Message> statusMessageFuture = interactionService.editOriginal(statusMessageToSend, interactionHook);
CompletableFuture<Void> deletionFuture = new CompletableFuture<>();
CompletableFuture<Void> retrievalFuture = CompletableFuture.allOf(historyFuture, statusMessageFuture);
retrievalFuture.thenAccept(voidParam -> {
try {
List<Message> retrievedHistory = historyFuture.get().getRetrievedHistory();
List<Message> messagesToDeleteNow = filterMessagesToDelete(retrievedHistory, purgedMember);
if(messagesToDeleteNow.isEmpty()) {
log.warn("No messages found to delete, all were filtered.");
deletionFuture.completeExceptionally(new NoMessageFoundException());
return;
}
Message latestMessage = messagesToDeleteNow.get(messagesToDeleteNow.size() - 1);
log.debug("Deleting {} messages directly", messagesToDeleteNow.size());
int newCurrentCount = currentCount + messagesToDeleteNow.size();
int newAmountToDelete = amountToDelete - PURGE_MAX_MESSAGES;
Consumer<Void> consumer = deletionConsumer(newAmountToDelete, channel, purgedMember, totalCount, newCurrentCount, deletionFuture, interactionHook, latestMessage);
if (messagesToDeleteNow.size() > 1) {
bulkDeleteMessages(channel, deletionFuture, messagesToDeleteNow, consumer);
} else if (messagesToDeleteNow.size() == 1) {
messageService.deleteMessageWithAction(latestMessage).queue(consumer, deletionFuture::completeExceptionally);
}
} catch (Exception e) {
log.warn("Failed to purge messages.", e);
deletionFuture.completeExceptionally(e);
}
}).exceptionally(throwable -> {
log.warn("Failed to fetch messages.", throwable);
return null;
});
return CompletableFuture.allOf(retrievalFuture, deletionFuture);
}
@PostConstruct
public void postConstruct() {
metricService.registerCounter(PURGE_MESSAGE_COUNTER_METRIC, "Amount of messages deleted by purge.");

View File

@@ -11,6 +11,7 @@ import dev.sheldan.abstracto.core.service.management.DefaultConfigManagementServ
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.core.utils.SnowflakeUtils;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.config.feature.WarningDecayFeatureConfig;
import dev.sheldan.abstracto.moderation.config.feature.WarningFeatureConfig;
@@ -167,7 +168,17 @@ public class WarnServiceBean implements WarnService {
}
ServerUser warnedServerUser = ServerUser.fromAUserInAServer(warnedUser);
ServerUser warningServerUser = ServerUser.fromAUserInAServer(warnedUser);
ServerChannelMessage commandMessage = ServerChannelMessage.fromMessage(context.getMessage());
ServerChannelMessage commandMessage;
if(context.getMessage() != null) {
commandMessage = ServerChannelMessage.fromMessage(context.getMessage());
} else {
commandMessage = ServerChannelMessage
.builder()
.serverId(context.getGuild().getIdLong())
.channelId(context.getChannel().getIdLong())
.messageId(SnowflakeUtils.createSnowFlake())
.build();
}
warningCreatedListenerManager.sendWarningCreatedEvent(createdWarning.getWarnId(), warnedServerUser, warningServerUser, context.getReason(), commandMessage);
}

View File

@@ -4,6 +4,7 @@ import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.CounterService;
import dev.sheldan.abstracto.moderation.exception.UserNoteNotFoundException;
import dev.sheldan.abstracto.moderation.model.database.UserNote;
import dev.sheldan.abstracto.moderation.repository.UserNoteRepository;
import lombok.extern.slf4j.Slf4j;
@@ -45,9 +46,15 @@ public class UserNoteManagementServiceBean implements UserNoteManagementService
userNoteRepository.deleteByUserNoteId_IdAndUserNoteId_ServerId(id, server.getId());
}
@Override
public void deleteNote(UserNote userNote) {
log.info("Deleting user note directly with id {} in server {}.", userNote.getUserNoteId().getId(), userNote.getServer().getId());
userNoteRepository.delete(userNote);
}
@Override
public UserNote loadNote(Long serverId, Long userNoteId) {
return userNoteRepository.findByUserNoteId_IdAndUserNoteId_ServerId(userNoteId, serverId);
return userNoteRepository.findByUserNoteId_IdAndUserNoteId_ServerId(userNoteId, serverId).orElseThrow(UserNoteNotFoundException::new);
}
@Override

View File

@@ -1,9 +1,9 @@
package dev.sheldan.abstracto.moderation.service.management;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.moderation.exception.WarnNotFoundException;
import dev.sheldan.abstracto.moderation.model.database.Warning;
import dev.sheldan.abstracto.moderation.repository.WarnRepository;
import lombok.extern.slf4j.Slf4j;
@@ -86,7 +86,7 @@ public class WarnManagementServiceBean implements WarnManagementService {
@Override
public Warning findById(Long id, Long serverId) {
return findByIdOptional(id, serverId).orElseThrow(() -> new AbstractoRunTimeException("Warning not found."));
return findByIdOptional(id, serverId).orElseThrow(WarnNotFoundException::new);
}
@Override

View File

@@ -1,54 +0,0 @@
package dev.sheldan.abstracto.moderation.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.moderation.service.BanService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class BanTest {
@InjectMocks
private Ban testUnit;
@Mock
private BanService banService;
@Captor
private ArgumentCaptor<Member> banLogModelCaptor;
@Mock
private User bannedMember;
@Test
public void testBanWithReason() {
String customReason = "reason2";
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(bannedMember, customReason));
when(banService.banUserWithNotification(eq(bannedMember), eq(customReason), banLogModelCaptor.capture(), eq(0), 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 validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,72 +0,0 @@
package dev.sheldan.abstracto.moderation.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ResultState;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.moderation.service.management.UserNoteManagementService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class DeleteNoteTest {
@InjectMocks
private DeleteNote testUnit;
@Mock
private UserNoteManagementService userNoteManagementService;
@Mock
private TemplateService templateService;
@Mock
private ServerManagementService serverManagementService;
private static final Long NOTE_ID = 5L;
private static final Long SERVER_ID = 4L;
@Test
public void testDeleteExistingNote() {
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(NOTE_ID));
AServer server = Mockito.mock(AServer.class);
when(serverManagementService.loadServer(parameters.getGuild())).thenReturn(server);
when(userNoteManagementService.noteExists(NOTE_ID, server)).thenReturn(true);
CommandResult result = testUnit.execute(parameters);
CommandTestUtilities.checkSuccessfulCompletion(result);
verify(userNoteManagementService, times(1)).deleteNote(NOTE_ID, server);
}
@Test
public void testDeleteNotExistingNote() {
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(NOTE_ID));
AServer server = Mockito.mock(AServer.class);
when(parameters.getGuild().getIdLong()).thenReturn(SERVER_ID);
when(serverManagementService.loadServer(parameters.getGuild())).thenReturn(server);
when(userNoteManagementService.noteExists(NOTE_ID, server)).thenReturn(false);
when(templateService.renderSimpleTemplate(DeleteNote.NOTE_NOT_FOUND_EXCEPTION_TEMPLATE, SERVER_ID)).thenReturn("error");
CommandResult result = testUnit.execute(parameters);
Assert.assertEquals(ResultState.ERROR, result.getResult());
Assert.assertNotNull(result.getMessage());
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,54 +0,0 @@
package dev.sheldan.abstracto.moderation.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.moderation.model.database.Warning;
import dev.sheldan.abstracto.moderation.service.management.WarnManagementService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.Optional;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class DeleteWarningTest {
@InjectMocks
private DeleteWarning testUnit;
@Mock
private WarnManagementService warnManagementService;
private static final Long WARN_ID = 5L;
private static final Long SERVER_ID = 1L;
@Test
public void testDeleteExistingWarning() {
Warning existingWarning = Mockito.mock(Warning.class);
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(WARN_ID));
when(parameters.getGuild().getIdLong()).thenReturn(SERVER_ID);
when(warnManagementService.findByIdOptional(WARN_ID, SERVER_ID)).thenReturn(Optional.of(existingWarning));
CommandResult result = testUnit.execute(parameters);
verify(warnManagementService, times(1)).deleteWarning(existingWarning);
CommandTestUtilities.checkSuccessfulCompletion(result);
}
@Test
public void testDeleteNotExistingWarning() {
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(WARN_ID));
CommandResult result = testUnit.execute(parameters);
CommandTestUtilities.checkSuccessfulCompletion(result);
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,60 +0,0 @@
package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.moderation.config.posttarget.ModerationPostTarget;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class BanServiceBeanTest {
public static final String REASON = "reason";
private static final Long SERVER_ID = 4L;
private static final Long USER_ID = 5L;
@InjectMocks
private BanServiceBean testUnit;
@Mock
private TemplateService templateService;
@Mock
private PostTargetService postTargetService;
@Mock
private Message message;
@Test
public void testBanMemberWithLog() {
Member memberToBan = Mockito.mock(Member.class);
User user = Mockito.mock(User.class);
when(memberToBan.getUser()).thenReturn(user);
Member banningMember = Mockito.mock(Member.class);
Guild mockedGuild = Mockito.mock(Guild.class);
when(memberToBan.getGuild()).thenReturn(mockedGuild);
when(mockedGuild.getIdLong()).thenReturn(SERVER_ID);
AuditableRestAction mockedAction = mock(AuditableRestAction.class);
when(mockedAction.submit()).thenReturn(CompletableFuture.completedFuture(null));
when(mockedGuild.ban(user, 0, REASON)).thenReturn(mockedAction);
MessageToSend mockedMessage = Mockito.mock(MessageToSend.class);
when(templateService.renderEmbedTemplate(eq(BanServiceBean.BAN_LOG_TEMPLATE), any(), eq(SERVER_ID))).thenReturn(mockedMessage);
testUnit.banMemberWithNotification(memberToBan, REASON, banningMember, 0, message);
verify(mockedGuild, times(1)).ban(user, 0, REASON);
verify(postTargetService, times(1)).sendEmbedInPostTarget(mockedMessage, ModerationPostTarget.BAN_LOG, SERVER_ID);
}
}