[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

@@ -4,16 +4,21 @@ 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.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentSlashCommandNames;
import dev.sheldan.abstracto.entertainment.model.command.ChooseResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -25,30 +30,81 @@ import java.util.concurrent.CompletableFuture;
public class Choose extends AbstractConditionableCommand {
public static final String CHOOSE_RESPONSE_TEMPLATE_KEY = "choose_response";
private static final String CHOOSE_COMMAND = "choose";
private static final String TEXT_PARAMETER = "text";
private static final int CHOICES_SIZE = 5;
@Autowired
private EntertainmentService entertainmentService;
@Autowired
private ChannelService channelService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<String> choices = (List) commandContext.getParameters().getParameters().get(0);
String choice = entertainmentService.takeChoice(choices, commandContext.getAuthor());
ChooseResponseModel responseModel = (ChooseResponseModel) ContextConverter.slimFromCommandContext(commandContext, ChooseResponseModel.class);
responseModel.setChosenValue(choice);
ChooseResponseModel responseModel = ChooseResponseModel
.builder()
.chosenValue(choice)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(CHOOSE_RESPONSE_TEMPLATE_KEY, responseModel, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
List<String> choices = new ArrayList<>();
for (int i = 0; i < CHOICES_SIZE; i++) {
if(slashCommandParameterService.hasCommandOption(TEXT_PARAMETER + "_" + i, event)) {
String choice = slashCommandParameterService.getCommandOption(TEXT_PARAMETER + "_" + i, event, String.class);
choices.add(choice);
}
}
String choice = entertainmentService.takeChoice(choices, event.getMember());
ChooseResponseModel responseModel = ChooseResponseModel
.builder()
.chosenValue(choice)
.build();
return interactionService.replyEmbed(CHOOSE_RESPONSE_TEMPLATE_KEY, responseModel, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("text").type(String.class).templated(true).remainder(true).isListParam(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter textParameter = Parameter
.builder()
.name(TEXT_PARAMETER)
.type(String.class)
.templated(true)
.remainder(true)
.listSize(CHOICES_SIZE)
.isListParam(true)
.build();
parameters.add(textParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(EntertainmentSlashCommandNames.UTILITY)
.commandName(CHOOSE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("choose")
.name(CHOOSE_COMMAND)
.async(true)
.slashCommandConfig(slashCommandConfig)
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
.templated(true)
.supportsEmbedException(true)

View File

@@ -4,17 +4,23 @@ 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.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentSlashCommandNames;
import dev.sheldan.abstracto.entertainment.model.command.EightBallResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -26,6 +32,8 @@ import java.util.concurrent.CompletableFuture;
public class EightBall extends AbstractConditionableCommand {
public static final String EIGHT_BALL_RESPONSE_TEMPLATE_KEY = "eight_ball_response";
public static final String BALL_COMMAND = "8Ball";
public static final String TEXT_PARAMETER = "text";
@Autowired
private EntertainmentService entertainmentService;
@@ -35,24 +43,65 @@ public class EightBall extends AbstractConditionableCommand {
@Autowired
private ChannelService channelService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
String text = (String) commandContext.getParameters().getParameters().get(0);
String chosenKey = entertainmentService.getEightBallValue(text);
EightBallResponseModel responseModel = (EightBallResponseModel) ContextConverter.slimFromCommandContext(commandContext, EightBallResponseModel.class);
responseModel.setChosenKey(chosenKey);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(EIGHT_BALL_RESPONSE_TEMPLATE_KEY, responseModel, commandContext.getChannel()))
MessageToSend messageToSend = getMessageToSend(text, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String text = slashCommandParameterService.getCommandOption(TEXT_PARAMETER, event, String.class);
MessageToSend messageToSend = getMessageToSend(text, event.getGuild().getIdLong());
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
private MessageToSend getMessageToSend(String text, Long serverId) {
String chosenKey = entertainmentService.getEightBallValue(text);
EightBallResponseModel responseModel = EightBallResponseModel
.builder()
.chosenKey(chosenKey)
.build();
return templateService.renderEmbedTemplate(EIGHT_BALL_RESPONSE_TEMPLATE_KEY, responseModel, serverId);
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("text").type(String.class).templated(true).remainder(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter textParameter = Parameter
.builder()
.name(TEXT_PARAMETER)
.type(String.class)
.templated(true)
.remainder(true)
.build();
parameters.add(textParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(EntertainmentSlashCommandNames.UTILITY)
.commandName(BALL_COMMAND)
.build();
return CommandConfiguration.builder()
.name("8Ball")
.name(BALL_COMMAND)
.async(true)
.slashCommandConfig(slashCommandConfig)
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
.templated(true)
.supportsEmbedException(true)

View File

@@ -4,16 +4,23 @@ 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.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelService;
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.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentSlashCommandNames;
import dev.sheldan.abstracto.entertainment.model.command.LoveCalcResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -25,6 +32,9 @@ import java.util.concurrent.CompletableFuture;
public class LoveCalc extends AbstractConditionableCommand {
public static final String LOVE_CALC_RESPONSE_TEMPLATE_KEY = "loveCalc_response";
public static final String FIRST_SUBJECT_PARAMETER = "firstSubject";
public static final String SECOND_SUBJECT_PARAMETER = "secondSubject";
public static final String LOVE_CALC_COMMAND = "loveCalc";
@Autowired
private ChannelService channelService;
@@ -32,28 +42,77 @@ public class LoveCalc extends AbstractConditionableCommand {
@Autowired
private EntertainmentService entertainmentService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private TemplateService templateService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String firstPart = (String) parameters.get(0);
String secondPart = (String) parameters.get(1);
Integer rolled = entertainmentService.getLoveCalcValue(firstPart, secondPart);
LoveCalcResponseModel model = (LoveCalcResponseModel) ContextConverter.slimFromCommandContext(commandContext, LoveCalcResponseModel.class);
model.setRolled(rolled);
model.setFirstPart(firstPart);
model.setSecondPart(secondPart);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(LOVE_CALC_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
MessageToSend messageToSend = getMessageToSend(commandContext.getGuild().getIdLong(), firstPart, secondPart);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String firstPart = slashCommandParameterService.getCommandOption(FIRST_SUBJECT_PARAMETER, event, String.class);
String secondPart = slashCommandParameterService.getCommandOption(SECOND_SUBJECT_PARAMETER, event, String.class);
MessageToSend messageToSend = getMessageToSend(event.getGuild().getIdLong(), firstPart, secondPart);
return interactionService.replyMessageToSend(messageToSend, event.getInteraction())
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
private MessageToSend getMessageToSend(Long serverId, String firstPart, String secondPart) {
Integer rolled = entertainmentService.getLoveCalcValue(firstPart, secondPart);
LoveCalcResponseModel model = LoveCalcResponseModel
.builder()
.rolled(rolled)
.firstPart(firstPart)
.secondPart(secondPart)
.build();
return templateService.renderEmbedTemplate(LOVE_CALC_RESPONSE_TEMPLATE_KEY, model, serverId);
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("firstSubject").type(String.class).templated(true).build());
parameters.add(Parameter.builder().name("secondSubject").type(String.class).templated(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter firstSubjectParameter = Parameter
.builder()
.name(FIRST_SUBJECT_PARAMETER)
.type(String.class)
.templated(true)
.build();
parameters.add(firstSubjectParameter);
Parameter secondSubjectParameter = Parameter
.builder()
.name(SECOND_SUBJECT_PARAMETER)
.type(String.class)
.templated(true)
.build();
parameters.add(secondSubjectParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(EntertainmentSlashCommandNames.UTILITY)
.commandName(LOVE_CALC_COMMAND)
.build();
return CommandConfiguration.builder()
.name("loveCalc")
.name(LOVE_CALC_COMMAND)
.slashCommandConfig(slashCommandConfig)
.async(true)
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
.templated(true)

View File

@@ -4,19 +4,23 @@ 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.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.execution.ContextConverter;
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.ConfigService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureConfig;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentSlashCommandNames;
import dev.sheldan.abstracto.entertainment.model.command.RollResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -29,6 +33,9 @@ import java.util.concurrent.CompletableFuture;
public class Roll extends AbstractConditionableCommand {
public static final String ROLL_RESPONSE_TEMPLATE_KEY = "roll_response";
private static final String ROLL_COMMAND = "roll";
private static final String LOW_PARAMETER = "low";
private static final String HIGH_PARAMETER = "high";
@Autowired
private ChannelService channelService;
@@ -39,6 +46,12 @@ public class Roll extends AbstractConditionableCommand {
@Autowired
private ConfigService configService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
@@ -52,20 +65,73 @@ public class Roll extends AbstractConditionableCommand {
}
Integer rolled = entertainmentService.calculateRollResult(low, high);
RollResponseModel model = (RollResponseModel) ContextConverter.slimFromCommandContext(commandContext, RollResponseModel.class);
model.setRolled(rolled);
RollResponseModel model = RollResponseModel
.builder()
.rolled(rolled)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(ROLL_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Integer low;
if(slashCommandParameterService.hasCommandOption(LOW_PARAMETER, event)) {
low = slashCommandParameterService.getCommandOption(LOW_PARAMETER, event, Integer.class);
} else {
low = 1;
}
Integer high;
if(slashCommandParameterService.hasCommandOption(HIGH_PARAMETER, event)) {
high = slashCommandParameterService.getCommandOption(HIGH_PARAMETER, event, Integer.class);
} else {
high = configService.getLongValueOrConfigDefault(EntertainmentFeatureConfig.ROLL_DEFAULT_HIGH_KEY, event.getGuild().getIdLong()).intValue();
}
Integer rolled = entertainmentService.calculateRollResult(low, high);
RollResponseModel model = RollResponseModel
.builder()
.rolled(rolled)
.build();
return interactionService.replyEmbed(ROLL_RESPONSE_TEMPLATE_KEY, model, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("high").type(Integer.class).templated(true).validators(Arrays.asList(MinIntegerValueValidator.min(2L))).optional(true).build());
parameters.add(Parameter.builder().name("low").type(Integer.class).templated(true).validators(Arrays.asList(MinIntegerValueValidator.min(0L))).optional(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter highParameter = Parameter
.builder()
.name(HIGH_PARAMETER)
.type(Integer.class)
.templated(true)
.validators(Arrays.asList(MinIntegerValueValidator.min(2L)))
.optional(true)
.build();
parameters.add(highParameter);
Parameter lowParameter = Parameter
.builder()
.name(LOW_PARAMETER)
.type(Integer.class)
.templated(true)
.validators(Arrays.asList(MinIntegerValueValidator.min(0L)))
.optional(true)
.build();
parameters.add(lowParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(EntertainmentSlashCommandNames.UTILITY)
.commandName(ROLL_COMMAND)
.build();
return CommandConfiguration.builder()
.name("roll")
.name(ROLL_COMMAND)
.slashCommandConfig(slashCommandConfig)
.async(true)
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
.templated(true)

View File

@@ -4,16 +4,19 @@ 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.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
import dev.sheldan.abstracto.entertainment.config.EntertainmentSlashCommandNames;
import dev.sheldan.abstracto.entertainment.model.command.RouletteResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -25,28 +28,57 @@ import java.util.concurrent.CompletableFuture;
public class Roulette extends AbstractConditionableCommand {
public static final String ROULETTE_RESPONSE_TEMPLATE_KEY = "roulette_response";
public static final String ROULETTE_COMMAND = "roulette";
@Autowired
private ChannelService channelService;
@Autowired
private EntertainmentService entertainmentService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
boolean rouletteResult = entertainmentService.executeRoulette(commandContext.getAuthor());
RouletteResponseModel responseModel = (RouletteResponseModel) ContextConverter.slimFromCommandContext(commandContext, RouletteResponseModel.class);
responseModel.setResult(rouletteResult);
RouletteResponseModel responseModel = RouletteResponseModel
.builder()
.result(rouletteResult)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(ROULETTE_RESPONSE_TEMPLATE_KEY, responseModel, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
boolean rouletteResult = entertainmentService.executeRoulette(event.getMember());
RouletteResponseModel responseModel = RouletteResponseModel
.builder()
.result(rouletteResult)
.build();
return interactionService.replyEmbed(ROULETTE_RESPONSE_TEMPLATE_KEY, responseModel, event)
.thenApply(interactionHook -> 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(EntertainmentSlashCommandNames.UTILITY)
.commandName(ROULETTE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("roulette")
.name(ROULETTE_COMMAND)
.async(true)
.slashCommandConfig(slashCommandConfig)
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
.templated(true)
.supportsEmbedException(true)

View File

@@ -1,56 +0,0 @@
package dev.sheldan.abstracto.entertainment.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.entertainment.model.command.ChooseResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class ChooseTest {
@InjectMocks
private Choose testUnit;
@Mock
private EntertainmentService entertainmentService;
@Mock
private ChannelService channelService;
@Captor
private ArgumentCaptor<ChooseResponseModel> responseModelArgumentCaptor;
@Test
public void executeChooseCommand() {
List<String> choices = Arrays.asList("choice1", "choice2");
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(choices));
when(entertainmentService.takeChoice(choices, parameters.getAuthor())).thenReturn(choices.get(0));
when(channelService.sendEmbedTemplateInTextChannelList(eq(Choose.CHOOSE_RESPONSE_TEMPLATE_KEY), responseModelArgumentCaptor.capture(), eq(parameters.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
Assert.assertEquals(choices.get(0), responseModelArgumentCaptor.getValue().getChosenValue());
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,57 +0,0 @@
package dev.sheldan.abstracto.entertainment.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.entertainment.model.command.EightBallResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class EightBallTest {
@InjectMocks
private EightBall testUnit;
@Mock
private EntertainmentService entertainmentService;
@Mock
private ChannelService channelService;
@Captor
private ArgumentCaptor<EightBallResponseModel> responseModelArgumentCaptor;
@Test
public void execute8BallCommand() {
String inputText = "text";
String chosenKey = "key";
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(inputText));
when(entertainmentService.getEightBallValue(inputText)).thenReturn(chosenKey);
when(channelService.sendEmbedTemplateInTextChannelList(eq(EightBall.EIGHT_BALL_RESPONSE_TEMPLATE_KEY), responseModelArgumentCaptor.capture(), eq(parameters.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
Assert.assertEquals(chosenKey, responseModelArgumentCaptor.getValue().getChosenKey());
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,61 +0,0 @@
package dev.sheldan.abstracto.entertainment.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.entertainment.model.command.LoveCalcResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.entertainment.command.LoveCalc.LOVE_CALC_RESPONSE_TEMPLATE_KEY;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class LoveCalcTest {
@InjectMocks
private LoveCalc testUnit;
@Mock
private EntertainmentService entertainmentService;
@Mock
private ChannelService channelService;
@Captor
private ArgumentCaptor<LoveCalcResponseModel> responseModelArgumentCaptor;
@Test
public void execute8BallCommand() {
String inputText = "text";
String inputText2 = "text2";
Integer loveResult = 2;
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(inputText, inputText2));
when(entertainmentService.getLoveCalcValue(inputText, inputText2)).thenReturn(loveResult);
when(channelService.sendEmbedTemplateInTextChannelList(eq(LOVE_CALC_RESPONSE_TEMPLATE_KEY), responseModelArgumentCaptor.capture(), eq(parameters.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
Assert.assertEquals(loveResult, responseModelArgumentCaptor.getValue().getRolled());
Assert.assertEquals(inputText, responseModelArgumentCaptor.getValue().getFirstPart());
Assert.assertEquals(inputText2, responseModelArgumentCaptor.getValue().getSecondPart());
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,87 +0,0 @@
package dev.sheldan.abstracto.entertainment.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.entertainment.model.command.RollResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureConfig.ROLL_DEFAULT_HIGH_KEY;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class RollTest {
@InjectMocks
private Roll testUnit;
@Mock
private EntertainmentService entertainmentService;
@Mock
private ChannelService channelService;
@Mock
private ConfigService configService;
@Captor
private ArgumentCaptor<RollResponseModel> responseModelArgumentCaptor;
@Test
public void executeWithNoParameter() {
CommandContext noParameters = CommandTestUtilities.getNoParameters();
Integer result = 4;
Long serverId = 3L;
Integer max = 10;
when(noParameters.getGuild().getIdLong()).thenReturn(serverId);
when(configService.getLongValueOrConfigDefault(ROLL_DEFAULT_HIGH_KEY, serverId)).thenReturn(max.longValue());
when(entertainmentService.calculateRollResult(1, max)).thenReturn(result);
when(channelService.sendEmbedTemplateInTextChannelList(eq(Roll.ROLL_RESPONSE_TEMPLATE_KEY), responseModelArgumentCaptor.capture(), eq(noParameters.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
CompletableFuture<CommandResult> futureResult = testUnit.executeAsync(noParameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(futureResult);
Assert.assertEquals(4, responseModelArgumentCaptor.getValue().getRolled().intValue());
}
@Test
public void executeWithHighParameter() {
CommandContext noParameters = CommandTestUtilities.getWithParameters(Arrays.asList(20));
Integer result = 4;
when(entertainmentService.calculateRollResult(1, 20)).thenReturn(result);
when(channelService.sendEmbedTemplateInTextChannelList(eq(Roll.ROLL_RESPONSE_TEMPLATE_KEY), responseModelArgumentCaptor.capture(), eq(noParameters.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
CompletableFuture<CommandResult> futureResult = testUnit.executeAsync(noParameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(futureResult);
Assert.assertEquals(4, responseModelArgumentCaptor.getValue().getRolled().intValue());
}
@Test
public void executeWithBothParameters() {
CommandContext noParameters = CommandTestUtilities.getWithParameters(Arrays.asList(20, 10));
Integer result = 4;
when(entertainmentService.calculateRollResult(10, 20)).thenReturn(result);
when(channelService.sendEmbedTemplateInTextChannelList(eq(Roll.ROLL_RESPONSE_TEMPLATE_KEY), responseModelArgumentCaptor.capture(), eq(noParameters.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
CompletableFuture<CommandResult> futureResult = testUnit.executeAsync(noParameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(futureResult);
Assert.assertEquals(4, responseModelArgumentCaptor.getValue().getRolled().intValue());
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,55 +0,0 @@
package dev.sheldan.abstracto.entertainment.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.entertainment.model.command.RouletteResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.concurrent.CompletableFuture;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class RouletteTest {
@InjectMocks
private Roulette testUnit;
@Mock
private EntertainmentService entertainmentService;
@Mock
private ChannelService channelService;
@Captor
private ArgumentCaptor<RouletteResponseModel> responseModelArgumentCaptor;
@Test
public void executeWithNoParameter() {
CommandContext noParameters = CommandTestUtilities.getNoParameters();
Boolean result = false;
when(entertainmentService.executeRoulette(noParameters.getAuthor())).thenReturn(result);
when(channelService.sendEmbedTemplateInTextChannelList(eq(Roulette.ROULETTE_RESPONSE_TEMPLATE_KEY), responseModelArgumentCaptor.capture(), eq(noParameters.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
CompletableFuture<CommandResult> futureResult = testUnit.executeAsync(noParameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(futureResult);
Assert.assertEquals(result, responseModelArgumentCaptor.getValue().getResult());
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.abstracto.entertainment.config;
public class EntertainmentSlashCommandNames {
public static final String ENTERTAINMENT = "entertainment";
public static final String UTILITY = "utility";
}

View File

@@ -1,13 +1,12 @@
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
@Getter
@Setter
@SuperBuilder
public class ChooseResponseModel extends SlimUserInitiatedServerContext {
@Builder
public class ChooseResponseModel {
private String chosenValue;
}

View File

@@ -1,13 +1,12 @@
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
@Getter
@Setter
@SuperBuilder
public class EightBallResponseModel extends SlimUserInitiatedServerContext {
@Builder
public class EightBallResponseModel {
private String chosenKey;
}

View File

@@ -1,14 +1,13 @@
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
@Getter
@Setter
@SuperBuilder
public class LoveCalcResponseModel extends SlimUserInitiatedServerContext {
@Builder
public class LoveCalcResponseModel {
private String firstPart;
private String secondPart;
private Integer rolled;

View File

@@ -1,13 +1,12 @@
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
@Getter
@Setter
@SuperBuilder
public class RollResponseModel extends SlimUserInitiatedServerContext {
@Builder
public class RollResponseModel {
private Integer rolled;
}

View File

@@ -1,13 +1,12 @@
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
@Getter
@Setter
@SuperBuilder
public class RouletteResponseModel extends SlimUserInitiatedServerContext {
@Builder
public class RouletteResponseModel {
private Boolean result;
}

View File

@@ -4,19 +4,26 @@ 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.ARole;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
import dev.sheldan.abstracto.experience.service.management.DisabledExpRoleManagementService;
import net.dv8tion.jda.api.entities.Role;
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;
/**
* Command used to add a role to the roles for which experience has been disabled.
@@ -24,21 +31,31 @@ import java.util.List;
@Component
public class DisableExpForRole extends AbstractConditionableCommand {
private static final String DISABLE_EXP_FOR_ROLE_COMMAND = "disableExpForRole";
private static final String DISABLE_EXP_FOR_ROLE_RESPONSE = "disableExpForRole_response";
private static final String ROLE_PARAMETER = "role";
@Autowired
private DisabledExpRoleManagementService disabledExpRoleManagementService;
@Autowired
private RoleManagementService roleManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
ARole role = (ARole) parameters.get(0);
ARole actualRole = roleManagementService.findRole(role.getId());
Role role = (Role) parameters.get(0);
ARole actualRole = roleManagementService.findRole(role.getIdLong());
if(!actualRole.getServer().getId().equals(commandContext.getGuild().getIdLong())) {
throw new EntityGuildMismatchException();
}
// as we mange experience disabled roles via the existence of them in a table, we should not do anything
// as we manage experience disabled roles via the existence of them in a table, we should not do anything
// in case it is used a second time as a disabled experience role
if(!disabledExpRoleManagementService.isExperienceDisabledForRole(actualRole)) {
disabledExpRoleManagementService.setRoleToBeDisabledForExp(actualRole);
@@ -46,15 +63,48 @@ public class DisableExpForRole extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Role role = slashCommandParameterService.getCommandOption(ROLE_PARAMETER, event, Role.class);
ARole actualRole = roleManagementService.findRole(role.getIdLong());
if(!actualRole.getServer().getId().equals(event.getGuild().getIdLong())) {
throw new EntityGuildMismatchException();
}
// as we manage experience disabled roles via the existence of them in a table, we should not do anything
// in case it is used a second time as a disabled experience role
if(!disabledExpRoleManagementService.isExperienceDisabledForRole(actualRole)) {
disabledExpRoleManagementService.setRoleToBeDisabledForExp(actualRole);
}
return interactionService.replyEmbed(DISABLE_EXP_FOR_ROLE_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("role").templated(true).type(ARole.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter roleParameter = Parameter
.builder()
.name(ROLE_PARAMETER)
.templated(true)
.type(Role.class)
.build();
List<Parameter> parameters = Arrays.asList(roleParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ExperienceSlashCommandNames.EXPERIENCE_CONFIG)
.commandName(DISABLE_EXP_FOR_ROLE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("disableExpForRole")
.name(DISABLE_EXP_FOR_ROLE_COMMAND)
.module(ExperienceModuleDefinition.EXPERIENCE)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -4,20 +4,26 @@ 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.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
import dev.sheldan.abstracto.experience.service.AUserExperienceService;
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;
/**
* Command used to disable the experience gain for a specific member
@@ -25,12 +31,21 @@ import java.util.List;
@Component
public class DisableExpGain extends AbstractConditionableCommand {
private static final String MEMBER_PARAMETER = "member";
private static final String DISABLE_EXP_GAIN_COMMAND = "disableExpGain";
private static final String DISABLE_EXP_GAIN_RESPONSE = "disableExpGain_response";
@Autowired
private AUserExperienceService aUserExperienceService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
Member para = (Member) commandContext.getParameters().getParameters().get(0);
@@ -42,14 +57,43 @@ public class DisableExpGain extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Member para = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER, event, Member.class);
if(!para.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(para);
aUserExperienceService.disableExperienceForUser(userInAServer);
return interactionService.replyEmbed(DISABLE_EXP_GAIN_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("member").templated(true).type(Member.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter memberParameter = Parameter
.builder()
.name(MEMBER_PARAMETER)
.templated(true)
.type(Member.class)
.build();
List<Parameter> parameters = Arrays.asList(memberParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ExperienceSlashCommandNames.EXPERIENCE_CONFIG)
.commandName(DISABLE_EXP_GAIN_COMMAND)
.build();
return CommandConfiguration.builder()
.name("disableExpGain")
.name(DISABLE_EXP_GAIN_COMMAND)
.module(ExperienceModuleDefinition.EXPERIENCE)
.slashCommandConfig(slashCommandConfig)
.causesReaction(true)
.supportsEmbedException(true)
.templated(true)

View File

@@ -4,19 +4,28 @@ 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.exception.SlashCommandParameterMissingException;
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.ARole;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
import dev.sheldan.abstracto.experience.service.management.DisabledExpRoleManagementService;
import net.dv8tion.jda.api.entities.Role;
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;
/**
* Command used to remove a role from the list of roles for which experience is disabled
@@ -24,12 +33,22 @@ import java.util.List;
@Component
public class EnableExpForRole extends AbstractConditionableCommand {
private static final String ENABLE_EXP_FOR_ROLE_COMMAND = "enableExpForRole";
private static final String ENABLE_EXP_FOR_ROLE_RESPONSE = "enableExpForRole_response";
private static final String ROLE_PARAMETER = "role";
@Autowired
private DisabledExpRoleManagementService disabledExpRoleManagementService;
@Autowired
private RoleManagementService roleManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
ARole role = (ARole) commandContext.getParameters().getParameters().get(0);
@@ -44,15 +63,51 @@ public class EnableExpForRole extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
ARole actualRole;
if(slashCommandParameterService.hasCommandOptionWithFullType(ROLE_PARAMETER, event, OptionType.ROLE)) {
Role role = slashCommandParameterService.getCommandOption(ROLE_PARAMETER, event, ARole.class, Role.class);
actualRole = roleManagementService.findRole(role.getIdLong());
} else if(slashCommandParameterService.hasCommandOptionWithFullType(ROLE_PARAMETER, event, OptionType.STRING)) {
String roleId = slashCommandParameterService.getCommandOption(ROLE_PARAMETER, event, ARole.class, String.class);
actualRole = roleManagementService.findRole(Long.parseLong(roleId));
} else {
throw new SlashCommandParameterMissingException(ROLE_PARAMETER);
}
if(disabledExpRoleManagementService.isExperienceDisabledForRole(actualRole)) {
disabledExpRoleManagementService.removeRoleToBeDisabledForExp(actualRole);
}
return interactionService.replyEmbed(ENABLE_EXP_FOR_ROLE_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("role").templated(true).type(ARole.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter roleParameter = Parameter
.builder()
.name(ROLE_PARAMETER)
.templated(true)
.type(ARole.class)
.build();
List<Parameter> parameters = Arrays.asList(roleParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ExperienceSlashCommandNames.EXPERIENCE_CONFIG)
.commandName(ENABLE_EXP_FOR_ROLE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("enableExpForRole")
.name(ENABLE_EXP_FOR_ROLE_COMMAND)
.module(ExperienceModuleDefinition.EXPERIENCE)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.causesReaction(true)
.supportsEmbedException(true)
.parameters(parameters)

View File

@@ -4,20 +4,26 @@ 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.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
import dev.sheldan.abstracto.experience.service.AUserExperienceService;
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;
/**
* Command to enable experience gain for a member
@@ -25,30 +31,69 @@ import java.util.List;
@Component
public class EnableExpGain extends AbstractConditionableCommand {
private static final String ENABLE_EXP_GAIN_COMMAND = "enableExpGain";
private static final String ENABLE_EXP_GAIN_RESPONSE = "enableExpGain_response";
private static final String MEMBER_PARAMETER = "member";
@Autowired
private AUserExperienceService aUserExperienceService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
Member para = (Member) commandContext.getParameters().getParameters().get(0);
if(!para.getGuild().equals(commandContext.getGuild())) {
Member member = (Member) commandContext.getParameters().getParameters().get(0);
if(!member.getGuild().equals(commandContext.getGuild())) {
throw new EntityGuildMismatchException();
}
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(para);
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(member);
aUserExperienceService.enableExperienceForUser(userInAServer);
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Member member = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER, event, Member.class);
if(!member.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(member);
aUserExperienceService.enableExperienceForUser(userInAServer);
return interactionService.replyEmbed(ENABLE_EXP_GAIN_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("member").templated(true).type(Member.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter memberParameter = Parameter
.builder()
.name(MEMBER_PARAMETER)
.templated(true)
.type(Member.class)
.build();
List<Parameter> parameters = Arrays.asList(memberParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ExperienceSlashCommandNames.EXPERIENCE_CONFIG)
.commandName(ENABLE_EXP_GAIN_COMMAND)
.build();
return CommandConfiguration.builder()
.name("enableExpGain")
.name(ENABLE_EXP_GAIN_COMMAND)
.slashCommandConfig(slashCommandConfig)
.module(ExperienceModuleDefinition.EXPERIENCE)
.causesReaction(true)
.supportsEmbedException(true)

View File

@@ -4,18 +4,24 @@ 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.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureConfig;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
import lombok.extern.slf4j.Slf4j;
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.List;
import java.util.concurrent.CompletableFuture;
/**
@@ -25,9 +31,19 @@ import java.util.List;
@Slf4j
public class ExpScale extends AbstractConditionableCommand {
private static final String EXP_SCALE_COMMAND = "expScale";
private static final String EXP_SCALE_RESPONSE = "expScale_response";
private static final String SCALE_PARAMETER = "scale";
@Autowired
private ConfigService configService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
Double scale = (Double) commandContext.getParameters().getParameters().get(0);
@@ -37,15 +53,42 @@ public class ExpScale extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Double newScale = slashCommandParameterService.getCommandOption(SCALE_PARAMETER, event, Double.class);
configService.setDoubleValue(ExperienceFeatureConfig.EXP_MULTIPLIER_KEY, event.getGuild().getIdLong(), newScale);
return interactionService.replyEmbed(EXP_SCALE_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("scale").templated(true).type(Double.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter newScale = Parameter
.builder()
.name(SCALE_PARAMETER)
.templated(true)
.type(Double.class)
.build();
parameters.add(newScale);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ExperienceSlashCommandNames.EXPERIENCE_CONFIG)
.commandName(EXP_SCALE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("expScale")
.name(EXP_SCALE_COMMAND)
.module(ExperienceModuleDefinition.EXPERIENCE)
.causesReaction(true)
.slashCommandConfig(slashCommandConfig)
.templated(true)
.supportsEmbedException(true)
.parameters(parameters)

View File

@@ -1,15 +1,13 @@
package dev.sheldan.abstracto.experience.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.execution.ContextConverter;
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.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
@@ -17,6 +15,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.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
import dev.sheldan.abstracto.experience.converter.LeaderBoardModelConverter;
import dev.sheldan.abstracto.experience.model.LeaderBoard;
import dev.sheldan.abstracto.experience.model.LeaderBoardEntry;
@@ -26,6 +25,8 @@ import dev.sheldan.abstracto.experience.service.AUserExperienceService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -42,6 +43,8 @@ import java.util.concurrent.CompletableFuture;
public class LeaderBoardCommand extends AbstractConditionableCommand {
public static final String LEADER_BOARD_POST_EMBED_TEMPLATE = "leaderboard_post";
private static final String LEADERBOARD_PARAMTER = "leaderboard";
private static final String PAGE_PARAMETER = "page";
@Autowired
private AUserExperienceService userExperienceService;
@@ -60,43 +63,89 @@ public class LeaderBoardCommand extends AbstractConditionableCommand {
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
// parameter is optional, in case its not present, we default to the 0th page
Integer page = !parameters.isEmpty() ? (Integer) parameters.get(0) : 1;
AServer server = serverManagementService.loadServer(commandContext.getGuild());
return getMessageToSend(commandContext.getAuthor(), page)
.thenCompose(messageToSend -> FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel())))
.thenApply(aVoid -> CommandResult.fromIgnored());
}
private CompletableFuture<MessageToSend> getMessageToSend(Member actorUser, Integer page) {
AServer server = serverManagementService.loadServer(actorUser.getGuild());
LeaderBoard leaderBoard = userExperienceService.findLeaderBoardData(server, page);
LeaderBoardModel leaderBoardModel = (LeaderBoardModel) ContextConverter.slimFromCommandContext(commandContext, LeaderBoardModel.class);
List<CompletableFuture> futures = new ArrayList<>();
CompletableFuture<List<LeaderBoardEntryModel>> completableFutures = converter.fromLeaderBoard(leaderBoard);
futures.add(completableFutures);
log.info("Rendering leaderboard for page {} in server {} for user {}.", page, commandContext.getAuthor().getId(), commandContext.getGuild().getId());
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
log.info("Rendering leaderboard for page {} in server {} for user {}.", page, actorUser.getId(), actorUser.getGuild().getId());
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(actorUser);
LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer);
CompletableFuture<List<LeaderBoardEntryModel>> userRankFuture = converter.fromLeaderBoardEntry(Arrays.asList(userRank));
futures.add(userRankFuture);
return FutureUtils.toSingleFuture(futures).thenCompose(aVoid -> {
List<LeaderBoardEntryModel> finalModels = completableFutures.join();
leaderBoardModel.setUserExperiences(finalModels);
leaderBoardModel.setUserExecuting(userRankFuture.join().get(0));
MessageToSend messageToSend = templateService.renderEmbedTemplate(LEADER_BOARD_POST_EMBED_TEMPLATE, leaderBoardModel, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()));
}).thenApply(aVoid -> CommandResult.fromIgnored());
LeaderBoardModel leaderBoardModel = LeaderBoardModel
.builder()
.userExperiences(finalModels)
.userExecuting(userRankFuture.join().get(0))
.build();
return CompletableFuture.completedFuture(templateService.renderEmbedTemplate(LEADER_BOARD_POST_EMBED_TEMPLATE, leaderBoardModel, actorUser.getGuild().getIdLong()));
});
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Integer page;
if(slashCommandParameterService.hasCommandOption(PAGE_PARAMETER, event)) {
page = slashCommandParameterService.getCommandOption(PAGE_PARAMETER, event, Integer.class);
} else {
page = 1;
}
return getMessageToSend(event.getMember(), page)
.thenCompose(messageToSend -> interactionService.replyMessageToSend(messageToSend, event))
.thenApply(aVoid -> CommandResult.fromIgnored());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
List<ParameterValidator> leaderBoardPageValidators = Arrays.asList(MinIntegerValueValidator.min(0L));
parameters.add(Parameter.builder().name("page").validators(leaderBoardPageValidators).optional(true).templated(true).type(Integer.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter pageParameter = Parameter
.builder()
.name(PAGE_PARAMETER)
.validators(leaderBoardPageValidators)
.optional(true)
.templated(true)
.type(Integer.class)
.build();
List<Parameter> parameters = Arrays.asList(pageParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ExperienceSlashCommandNames.EXPERIENCE)
.commandName(LEADERBOARD_PARAMTER)
.build();
return CommandConfiguration.builder()
.name("leaderboard")
.name(LEADERBOARD_PARAMTER)
.module(ExperienceModuleDefinition.EXPERIENCE)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -4,17 +4,23 @@ 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.ChannelService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
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.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
import dev.sheldan.abstracto.experience.model.template.LevelRole;
import dev.sheldan.abstracto.experience.model.template.LevelRolesModel;
import dev.sheldan.abstracto.experience.service.ExperienceRoleService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -27,6 +33,9 @@ import java.util.stream.Collectors;
@Component
public class LevelRoles extends AbstractConditionableCommand {
private static final String LEVEL_ROLES_COMMAND = "levelRoles";
private static final String LEVEL_ROLES_TEMPLATE_KEY = "levelRoles_response";
@Autowired
private ExperienceRoleService experienceRoleService;
@@ -36,29 +45,58 @@ public class LevelRoles extends AbstractConditionableCommand {
@Autowired
private ChannelService channelService;
private static final String LEVEL_ROLES_TEMPLATE_KEY = "levelRoles_response";
@Autowired
private TemplateService templateService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
AServer server = serverManagementService.loadServer(commandContext.getGuild());
MessageToSend messageToSend = getResponseModel(commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long serverId = event.getGuild().getIdLong();
MessageToSend messageToSend = getResponseModel(serverId);
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
private MessageToSend getResponseModel(Long serverId) {
AServer server = serverManagementService.loadServer(serverId);
List<LevelRole> levelRoles = experienceRoleService.loadLevelRoleConfigForServer(server);
levelRoles = levelRoles.stream().sorted(Comparator.comparingInt(LevelRole::getLevel).reversed()).collect(Collectors.toList());
LevelRolesModel model = LevelRolesModel
.builder()
.levelRoles(levelRoles)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(LEVEL_ROLES_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
return templateService.renderEmbedTemplate(LEVEL_ROLES_TEMPLATE_KEY, model, serverId);
}
@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(ExperienceSlashCommandNames.EXPERIENCE)
.commandName(LEVEL_ROLES_COMMAND)
.build();
return CommandConfiguration.builder()
.name("levelRoles")
.name(LEVEL_ROLES_COMMAND)
.module(ExperienceModuleDefinition.EXPERIENCE)
.async(true)
.slashCommandConfig(slashCommandConfig)
.templated(true)
.supportsEmbedException(true)
.parameters(parameters)

View File

@@ -4,22 +4,28 @@ 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.FullRole;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
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.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
import dev.sheldan.abstracto.experience.model.database.ADisabledExpRole;
import dev.sheldan.abstracto.experience.model.template.DisabledExperienceRolesModel;
import dev.sheldan.abstracto.experience.service.management.DisabledExpRoleManagementService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -34,6 +40,9 @@ import java.util.concurrent.CompletableFuture;
@Component
public class ListDisabledExperienceRoles extends AbstractConditionableCommand {
private static final String LIST_DISABLED_EXPERIENCE_ROLES_RESPONSE = "list_disabled_experience_roles";
private static final String LIST_DISABLED_EXPERIENCE_ROLES_COMMAND = "listDisabledExperienceRoles";
@Autowired
private DisabledExpRoleManagementService disabledExpRoleManagementService;
@@ -46,11 +55,35 @@ public class ListDisabledExperienceRoles extends AbstractConditionableCommand {
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
AServer server = serverManagementService.loadServer(commandContext.getGuild());
Long serverId = commandContext.getGuild().getIdLong();
MessageToSend messageToSend = getResponseModel(serverId, commandContext.getAuthor());
List<CompletableFuture<Message>> futures = channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel());
return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromIgnored());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long serverId = event.getGuild().getIdLong();
MessageToSend messageToSend = getResponseModel(serverId, event.getMember());
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
private MessageToSend getResponseModel(Long serverId, Member member) {
AServer server = serverManagementService.loadServer(serverId);
List<ADisabledExpRole> disabledRolesForServer = disabledExpRoleManagementService.getDisabledRolesForServer(server);
DisabledExperienceRolesModel disabledExperienceRolesModel = (DisabledExperienceRolesModel) ContextConverter.fromCommandContext(commandContext, DisabledExperienceRolesModel.class);
DisabledExperienceRolesModel disabledExperienceRolesModel = DisabledExperienceRolesModel
.builder()
.member(member)
.build();
disabledRolesForServer.forEach(aDisabledExpRole -> {
Role jdaRole = null;
if(!aDisabledExpRole.getRole().getDeleted()) {
@@ -63,18 +96,29 @@ public class ListDisabledExperienceRoles extends AbstractConditionableCommand {
.build();
disabledExperienceRolesModel.getRoles().add(role);
});
List<CompletableFuture<Message>> futures = channelService.sendEmbedTemplateInTextChannelList("list_disabled_experience_roles", disabledExperienceRolesModel, commandContext.getChannel());
return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromIgnored());
return templateService.renderEmbedTemplate(LIST_DISABLED_EXPERIENCE_ROLES_RESPONSE, disabledExperienceRolesModel, serverId);
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
List<String> aliases = Arrays.asList("lsDisEpRoles");
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ExperienceSlashCommandNames.EXPERIENCE_CONFIG)
.commandName(LIST_DISABLED_EXPERIENCE_ROLES_COMMAND)
.build();
return CommandConfiguration.builder()
.name("listDisabledExperienceRoles")
.name(LIST_DISABLED_EXPERIENCE_ROLES_COMMAND)
.module(ExperienceModuleDefinition.EXPERIENCE)
.slashCommandConfig(slashCommandConfig)
.templated(true)
.supportsEmbedException(true)
.causesReaction(true)

View File

@@ -4,15 +4,19 @@ 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.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
import dev.sheldan.abstracto.experience.converter.LeaderBoardModelConverter;
import dev.sheldan.abstracto.experience.model.LeaderBoardEntry;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
@@ -25,11 +29,11 @@ import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -42,6 +46,8 @@ import java.util.concurrent.CompletableFuture;
public class Rank extends AbstractConditionableCommand {
public static final String RANK_POST_EMBED_TEMPLATE = "rank_post";
public static final String RANK_COMMAND = "rank";
public static final String MEMBER_PARAMETER = "member";
@Autowired
private LeaderBoardModelConverter converter;
@@ -66,43 +72,89 @@ public class Rank extends AbstractConditionableCommand {
@Autowired
private UserExperienceManagementService userExperienceManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Member parameter = !parameters.isEmpty() ? (Member) parameters.get(0) : commandContext.getAuthor();
if(!parameter.getGuild().equals(commandContext.getGuild())) {
Member targetMember = !parameters.isEmpty() ? (Member) parameters.get(0) : commandContext.getAuthor();
if(!targetMember.getGuild().equals(commandContext.getGuild())) {
throw new EntityGuildMismatchException();
}
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(parameter);
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(targetMember);
LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer);
CompletableFuture<List<LeaderBoardEntryModel>> future = converter.fromLeaderBoardEntry(Arrays.asList(userRank));
RankModel rankModel = RankModel
.builder()
.member(parameter)
.member(targetMember)
.build();
return future.thenCompose(leaderBoardEntryModel ->
self.renderAndSendRank(commandContext, parameter, rankModel, leaderBoardEntryModel.get(0))
).thenApply(result -> CommandResult.fromIgnored());
return future.thenCompose(leaderBoardEntryModel -> {
MessageToSend messageToSend = self.renderMessageToSend(targetMember, rankModel, leaderBoardEntryModel.get(0));
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()));
}).thenApply(result -> CommandResult.fromIgnored());
}
@Transactional
public CompletableFuture<Void> renderAndSendRank(CommandContext commandContext, Member toRender, RankModel rankModel, LeaderBoardEntryModel leaderBoardEntryModel) {
public MessageToSend renderMessageToSend(Member toRender, RankModel rankModel, LeaderBoardEntryModel leaderBoardEntryModel) {
rankModel.setRankUser(leaderBoardEntryModel);
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(toRender);
AUserExperience experienceObj = userExperienceManagementService.findUserInServer(aUserInAServer);
log.info("Rendering rank for user {} in server {}.", toRender.getId(), commandContext.getGuild().getId());
log.info("Rendering rank for user {} in server {}.", toRender.getId(), toRender.getGuild().getId());
rankModel.setExperienceToNextLevel(experienceLevelService.calculateExperienceToNextLevel(experienceObj.getCurrentLevel().getLevel(), experienceObj.getExperience()));
MessageToSend messageToSend = templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()));
return templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel, toRender.getGuild().getIdLong());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Member targetMember;
if(slashCommandParameterService.hasCommandOption(MEMBER_PARAMETER, event)) {
targetMember = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER, event, Member.class);
} else {
targetMember = event.getMember();
}
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(targetMember);
LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer);
CompletableFuture<List<LeaderBoardEntryModel>> future = converter.fromLeaderBoardEntry(Arrays.asList(userRank));
RankModel rankModel = RankModel
.builder()
.member(targetMember)
.build();
return future.thenCompose(leaderBoardEntryModel -> {
MessageToSend messageToSend = self.renderMessageToSend(targetMember, rankModel, leaderBoardEntryModel.get(0));
return interactionService.replyMessageToSend(messageToSend, event);
}).thenApply(result -> 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();
List<Parameter> parameters = Arrays.asList(memberParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ExperienceSlashCommandNames.EXPERIENCE)
.commandName(RANK_COMMAND)
.build();
return CommandConfiguration.builder()
.name("rank")
.name(RANK_COMMAND)
.slashCommandConfig(slashCommandConfig)
.module(ExperienceModuleDefinition.EXPERIENCE)
.templated(true)
.async(true)

View File

@@ -1,64 +0,0 @@
package dev.sheldan.abstracto.experience.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.experience.service.management.DisabledExpRoleManagementService;
import lombok.extern.slf4j.Slf4j;
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)
@Slf4j
public class DisableExpForRoleTest {
@InjectMocks
private DisableExpForRole testUnit;
@Mock
private RoleManagementService roleManagementService;
@Mock
private DisabledExpRoleManagementService disabledExpRoleManagementService;
@Test
public void testExecuteCommandForNotDisabledRole() {
executeDisableExpForRoleTest(false, 1);
}
@Test
public void testExecuteCommandForDisabledRole() {
executeDisableExpForRoleTest(true, 0);
}
private void executeDisableExpForRoleTest(boolean value, int wantedNumberOfInvocations) {
ARole parameterRole = Mockito.mock(ARole.class);
ARole actualRole = Mockito.mock(ARole.class);
when(parameterRole.getId()).thenReturn(5L);
CommandContext context = CommandTestUtilities.getWithParameters(Arrays.asList(parameterRole));
AServer server = Mockito.mock(AServer.class);
when(actualRole.getServer()).thenReturn(server);
when(roleManagementService.findRole(parameterRole.getId())).thenReturn(actualRole);
when(disabledExpRoleManagementService.isExperienceDisabledForRole(actualRole)).thenReturn(value);
CommandResult result = testUnit.execute(context);
verify(disabledExpRoleManagementService, times(wantedNumberOfInvocations)).setRoleToBeDisabledForExp(actualRole);
CommandTestUtilities.checkSuccessfulCompletion(result);
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,93 +0,0 @@
package dev.sheldan.abstracto.experience.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.experience.converter.LeaderBoardModelConverter;
import dev.sheldan.abstracto.experience.model.LeaderBoard;
import dev.sheldan.abstracto.experience.model.LeaderBoardEntry;
import dev.sheldan.abstracto.experience.model.template.LeaderBoardEntryModel;
import dev.sheldan.abstracto.experience.model.template.LeaderBoardModel;
import dev.sheldan.abstracto.experience.service.AUserExperienceService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
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.ArrayList;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class LeaderBoardCommandTest {
@InjectMocks
private LeaderBoardCommand testUnit;
@Mock
private AUserExperienceService userExperienceService;
@Mock
private TemplateService templateService;
@Mock
private ChannelService channelService;
@Mock
private LeaderBoardModelConverter converter;
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private ServerManagementService serverManagementService;
private static final Long SERVER_ID = 45L;
@Test
public void testLeaderBoardWithNoParameter() {
testLeaderBoardCommand(CommandTestUtilities.getNoParameters(), 1);
}
@Test
public void testLeaderBoardWithPageParameter() {
testLeaderBoardCommand(CommandTestUtilities.getWithParameters(Arrays.asList(5)), 5);
}
private void testLeaderBoardCommand(CommandContext context, int expectedPage) {
when(context.getGuild().getIdLong()).thenReturn(SERVER_ID);
LeaderBoard leaderBoard = Mockito.mock(LeaderBoard.class);
AServer server = Mockito.mock(AServer.class);
when(serverManagementService.loadServer(context.getGuild())).thenReturn(server);
AUserInAServer userInAServer = Mockito.mock(AUserInAServer.class);
when(userInServerManagementService.loadOrCreateUser(context.getAuthor())).thenReturn(userInAServer);
when(userExperienceService.findLeaderBoardData(server, expectedPage)).thenReturn(leaderBoard);
when(converter.fromLeaderBoard(leaderBoard)).thenReturn(CompletableFuture.completedFuture(null));
LeaderBoardEntry executingUserRank = Mockito.mock(LeaderBoardEntry.class);
when(userExperienceService.getRankOfUserInServer(userInAServer)).thenReturn(executingUserRank);
LeaderBoardEntryModel leaderBoardEntryModel = Mockito.mock(LeaderBoardEntryModel.class);
when(converter.fromLeaderBoardEntry(Arrays.asList(executingUserRank))).thenReturn(CompletableFuture.completedFuture(Arrays.asList(leaderBoardEntryModel)));
MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
when(templateService.renderEmbedTemplate(eq(LeaderBoardCommand.LEADER_BOARD_POST_EMBED_TEMPLATE), any(LeaderBoardModel.class), eq(SERVER_ID))).thenReturn(messageToSend);
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
verify(channelService, times(1)).sendMessageToSendToChannel(messageToSend, context.getChannel());
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,90 +0,0 @@
package dev.sheldan.abstracto.experience.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.RoleService;
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.experience.model.database.ADisabledExpRole;
import dev.sheldan.abstracto.experience.model.template.DisabledExperienceRolesModel;
import dev.sheldan.abstracto.experience.service.management.DisabledExpRoleManagementService;
import net.dv8tion.jda.api.entities.Role;
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.ArrayList;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class ListDisabledExperienceRolesTest {
@InjectMocks
private ListDisabledExperienceRoles testUnit;
@Mock
private DisabledExpRoleManagementService disabledExpRoleManagementService;
@Mock
private RoleService roleService;
@Mock
private ChannelService channelService;
@Mock
private ServerManagementService serverManagementService;
@Mock
private AServer server;
@Test
public void testCommandExecutionNoRolesFound() {
CommandContext context = CommandTestUtilities.getNoParameters();
when(serverManagementService.loadServer(context.getGuild())).thenReturn(server);
when(disabledExpRoleManagementService.getDisabledRolesForServer(server)).thenReturn(new ArrayList<>());
when(channelService.sendEmbedTemplateInTextChannelList(eq("list_disabled_experience_roles"),
any(DisabledExperienceRolesModel.class), eq(context.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
verify(roleService, times(0)).getRoleFromGuild(any(ARole.class));
}
@Test
public void testCommandExecutionRolesFound() {
CommandContext context = CommandTestUtilities.getNoParameters();
ADisabledExpRole disabledExpRole1 = Mockito.mock(ADisabledExpRole.class);
ADisabledExpRole disabledExpRole2 = Mockito.mock(ADisabledExpRole.class);
when(disabledExpRoleManagementService.getDisabledRolesForServer(server)).thenReturn(Arrays.asList(disabledExpRole1, disabledExpRole2));
Role role1 = Mockito.mock(Role.class);
Role role2 = Mockito.mock(Role.class);
ARole aRole = Mockito.mock(ARole.class);
ARole aRole2 = Mockito.mock(ARole.class);
when(roleService.getRoleFromGuild(aRole)).thenReturn(role1);
when(roleService.getRoleFromGuild(aRole2)).thenReturn(role2);
when(aRole.getDeleted()).thenReturn(false);
when(aRole2.getDeleted()).thenReturn(false);
when(disabledExpRole1.getRole()).thenReturn(aRole);
when(disabledExpRole2.getRole()).thenReturn(aRole2);
when(serverManagementService.loadServer(context.getGuild())).thenReturn(server);
when(channelService.sendEmbedTemplateInTextChannelList(eq("list_disabled_experience_roles"),
any(DisabledExperienceRolesModel.class), eq(context.getChannel()))).thenReturn(CommandTestUtilities.messageFutureList());
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,110 +0,0 @@
package dev.sheldan.abstracto.experience.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
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.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.experience.converter.LeaderBoardModelConverter;
import dev.sheldan.abstracto.experience.model.LeaderBoardEntry;
import dev.sheldan.abstracto.experience.model.database.AExperienceLevel;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.model.template.LeaderBoardEntryModel;
import dev.sheldan.abstracto.experience.model.template.RankModel;
import dev.sheldan.abstracto.experience.service.AUserExperienceService;
import dev.sheldan.abstracto.experience.service.ExperienceLevelService;
import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.Member;
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.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.experience.command.Rank.RANK_POST_EMBED_TEMPLATE;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class RankTest {
@InjectMocks
private Rank testUnit;
@Mock
private LeaderBoardModelConverter converter;
@Mock
private TemplateService templateService;
@Mock
private AUserExperienceService userExperienceService;
@Mock
private ExperienceLevelService experienceLevelService;
@Mock
private ChannelService channelService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private Rank self;
private static final Long SERVER_ID = 4L;
@Mock
private AUserInAServer aUserInAServer;
@Mock
private UserExperienceManagementService userExperienceManagementService;
@Test
public void testRankExecution() {
CommandContext context = CommandTestUtilities.getNoParameters();
LeaderBoardEntry leaderBoardEntry = Mockito.mock(LeaderBoardEntry.class);
when(userInServerManagementService.loadOrCreateUser(context.getAuthor())).thenReturn(aUserInAServer);
when(userExperienceService.getRankOfUserInServer(aUserInAServer)).thenReturn(leaderBoardEntry);
LeaderBoardEntryModel leaderBoardEntryModel = Mockito.mock(LeaderBoardEntryModel.class);
when(context.getAuthor().getGuild()).thenReturn(context.getGuild());
when(converter.fromLeaderBoardEntry(Arrays.asList(leaderBoardEntry))).thenReturn(CompletableFuture.completedFuture(Arrays.asList(leaderBoardEntryModel)));
when(self.renderAndSendRank(eq(context), any(Member.class), any(RankModel.class), eq(leaderBoardEntryModel))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
}
@Test
public void testRenderAndSend() {
int currentLevelValue = 50;
long currentExperience = 50L;
AExperienceLevel currentLevel = Mockito.mock(AExperienceLevel.class);
when(currentLevel.getLevel()).thenReturn(currentLevelValue);
AUserExperience aUserExperience = Mockito.mock(AUserExperience.class);
when(aUserExperience.getCurrentLevel()).thenReturn(currentLevel);
when(aUserExperience.getExperience()).thenReturn(currentExperience);
CommandContext context = CommandTestUtilities.getNoParameters();
when(context.getGuild().getIdLong()).thenReturn(SERVER_ID);
RankModel rankModel = Mockito.mock(RankModel.class);
LeaderBoardEntryModel leaderBoardEntryModel = Mockito.mock(LeaderBoardEntryModel.class);
when(userInServerManagementService.loadOrCreateUser(context.getAuthor())).thenReturn(aUserInAServer);
when(userExperienceManagementService.findUserInServer(aUserInAServer)).thenReturn(aUserExperience);
when(experienceLevelService.calculateExperienceToNextLevel(currentLevelValue, currentExperience)).thenReturn(140L);
MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
when(templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel, SERVER_ID)).thenReturn(messageToSend);
when(channelService.sendMessageToSendToChannel(messageToSend, context.getChannel())).thenReturn(Arrays.asList(CompletableFuture.completedFuture(null)));
testUnit.renderAndSendRank(context, context.getAuthor(), rankModel, leaderBoardEntryModel).join();
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.abstracto.experience.config;
public class ExperienceSlashCommandNames {
public static final String EXPERIENCE = "experience";
public static final String EXPERIENCE_CONFIG = "experienceconfig";
}

View File

@@ -1,10 +1,9 @@
package dev.sheldan.abstracto.experience.model.template;
import dev.sheldan.abstracto.core.models.FullRole;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import java.util.ArrayList;
import java.util.List;
@@ -13,11 +12,12 @@ import java.util.List;
* Model used to render an overview of the roles for which experience gain has been disabled on the current server.
*/
@Getter
@SuperBuilder
public class DisabledExperienceRolesModel extends UserInitiatedServerContext {
@Builder
public class DisabledExperienceRolesModel {
/**
* A list of {@link FullRole roles} for which experience gain is disabled in this server
*/
@Builder.Default
private List<FullRole> roles = new ArrayList<>();
private Member member;
}

View File

@@ -50,9 +50,6 @@ public class MessageEmbedListenerTest {
@Mock
private MessageEmbedListener self;
@Mock
private GuildMemberMessageChannel context;
@Mock
private Message message;
@@ -65,6 +62,9 @@ public class MessageEmbedListenerTest {
@Mock
private Guild guild;
@Mock
private Member member;
private static final Long FIRST_SERVER_ID = 12L;
private static final Long SECOND_SERVER_ID = 13L;
private static final Long FIRST_CHANNEL_ID = 45L;
@@ -75,8 +75,10 @@ public class MessageEmbedListenerTest {
@Before
public void setup(){
when(guild.getIdLong()).thenReturn(FIRST_SERVER_ID);
when(context.getGuild()).thenReturn(guild);
when(context.getGuildChannel()).thenReturn(textChannel);
when(model.getMessage()).thenReturn(message);
when(message.getGuild()).thenReturn(guild);
when(message.getChannel()).thenReturn(textChannel);
when(message.getMember()).thenReturn(member);
}
@Test
@@ -126,9 +128,7 @@ public class MessageEmbedListenerTest {
when(foundLink.getMessageId()).thenReturn(FIRST_MESSAGE_ID);
when(foundLink.getServerId()).thenReturn(SECOND_SERVER_ID);
List<MessageEmbedLink> foundMessageLinks = Arrays.asList(foundLink);
Member author = Mockito.mock(Member.class);
when(context.getMember()).thenReturn(author);
when(userInServerManagementService.loadOrCreateUser(author)).thenReturn(userInAServer);
when(userInServerManagementService.loadOrCreateUser(member)).thenReturn(userInAServer);
when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks);
when(model.getMessage()).thenReturn(message);
testUnit.execute(model);
@@ -155,11 +155,8 @@ public class MessageEmbedListenerTest {
String completeMessage = firstText.concat(secondText);
when(message.getContentRaw()).thenReturn(completeMessage);
setupMessageConfig();
Member author = Mockito.mock(Member.class);
when(context.getMember()).thenReturn(author);
when(context.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(FIRST_SERVER_ID);
when(userInServerManagementService.loadOrCreateUser(author)).thenReturn(embeddingUser);
when(userInServerManagementService.loadOrCreateUser(member)).thenReturn(embeddingUser);
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage));
when(messageEmbedService.getLinksInMessage(completeMessage)).thenReturn(foundMessageLinks);
@@ -187,9 +184,7 @@ public class MessageEmbedListenerTest {
when(userInAServer.getUserInServerId()).thenReturn(USER_IN_SERVER_ID);
when(message.getContentRaw()).thenReturn(text);
setupMessageConfig();
Member author = Mockito.mock(Member.class);
when(context.getMember()).thenReturn(author);
when(userInServerManagementService.loadOrCreateUser(author)).thenReturn(userInAServer);
when(userInServerManagementService.loadOrCreateUser(member)).thenReturn(userInAServer);
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
CachedMessage secondCachedMessage = Mockito.mock(CachedMessage.class);
when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage));
@@ -206,8 +201,8 @@ public class MessageEmbedListenerTest {
public void testLoadUserAndEmbed() {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
long userId = 3L;
when(context.getGuildChannel()).thenReturn(textChannel);
when(messageEmbedService.embedLink(cachedMessage, textChannel, userId, context)).thenReturn(CompletableFuture.completedFuture(null));
when(message.getGuildChannel()).thenReturn(textChannel);
when(messageEmbedService.embedLink(eq(cachedMessage), eq(textChannel), eq(userId), any(GuildMemberMessageChannel.class))).thenReturn(CompletableFuture.completedFuture(null));
testUnit.embedSingleLink(message, userId, cachedMessage);
verify(metricService, times(1)).incrementCounter(any());
}
@@ -222,9 +217,7 @@ public class MessageEmbedListenerTest {
when(foundLink.getServerId()).thenReturn(FIRST_SERVER_ID);
when(foundLink.getChannelId()).thenReturn(FIRST_CHANNEL_ID);
List<MessageEmbedLink> foundMessageLinks = Arrays.asList(foundLink);
Member author = Mockito.mock(Member.class);
when(context.getMember()).thenReturn(author);
when(userInServerManagementService.loadOrCreateUser(author)).thenReturn(userInAServer);
when(userInServerManagementService.loadOrCreateUser(member)).thenReturn(userInAServer);
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(messageCache.getMessageFromCache(FIRST_SERVER_ID, FIRST_CHANNEL_ID, FIRST_MESSAGE_ID)).thenReturn(CompletableFuture.completedFuture(cachedMessage));
when(messageEmbedService.getLinksInMessage(text)).thenReturn(foundMessageLinks);

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);
}
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.moderation.config;
public class ModerationSlashCommandNames {
public static final String MODERATION = "moderation";
public static final String WARNINGS = "warnings";
public static final String USER_NOTES = "usernotes";
public static final String MUTE = "mute";
public static final String WARN_DECAY = "warningdecay";
public static final String WARNINGS_PUBLIC = "warningspublic";
}

View File

@@ -10,7 +10,6 @@ public enum ModerationFeatureDefinition implements FeatureDefinition {
MUTING("muting"),
AUTOMATIC_WARN_DECAY("warnDecay"),
USER_NOTES("userNotes"),
INVITE_FILTER("inviteFilter"),
REPORT_REACTIONS("reportReactions"),
INFRACTIONS("infractions")
;

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.moderation.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class UserNoteNotFoundException extends AbstractoTemplatableException {
@Override
public String getTemplateName() {
return "user_note_not_found_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.moderation.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class WarnNotFoundException extends AbstractoTemplatableException {
@Override
public String getTemplateName() {
return "warn_not_found_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.abstracto.moderation.model;
public enum BanResult {
NOTIFICATION_FAILED, SUCCESSFUL
}

View File

@@ -1,18 +1,19 @@
package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.Member;
/**
* Used when rendering the notification when a member was kicked. The template is: "kick_log_embed"
*/
@Getter
@SuperBuilder
@Builder
@Setter
public class KickLogModel extends SlimUserInitiatedServerContext {
public class KickLogModel {
/**
* The reason of the kick
*/
@@ -21,4 +22,7 @@ public class KickLogModel extends SlimUserInitiatedServerContext {
* The member being kicked
*/
private Member kickedUser;
private Member member;
private Guild guild;
private GuildMessageChannel channel;
}

View File

@@ -1,17 +1,18 @@
package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.FullUserInServer;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import java.util.List;
@SuperBuilder
@Builder
@Getter
@Setter
public class ListNotesModel extends UserInitiatedServerContext {
public class ListNotesModel {
private List<NoteEntryModel> userNotes;
private FullUserInServer specifiedUser;
private Member member;
}

View File

@@ -1,15 +1,14 @@
package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import java.util.List;
@Getter
@Setter
@SuperBuilder
public class MutesModel extends SlimUserInitiatedServerContext {
@Builder
public class MutesModel {
private List<MuteEntry> mutes;
}

View File

@@ -1,17 +1,17 @@
package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
/**
* Used to render the response of the myWarnings command. The template is: 'myWarnings_response_embed'
*/
@Getter
@Setter
@SuperBuilder
public class MyWarningsModel extends UserInitiatedServerContext {
@Builder
public class MyWarningsModel {
/**
* The total amount of warnings the member has
*/
@@ -20,4 +20,5 @@ public class MyWarningsModel extends UserInitiatedServerContext {
* The current (only active) amount of warnings the member has
*/
private Long currentWarnCount;
private Member member;
}

View File

@@ -1,19 +1,21 @@
package dev.sheldan.abstracto.moderation.model.template.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import dev.sheldan.abstracto.moderation.model.database.Warning;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
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.entities.Message;
/**
* Used when rendering the notification when a member was warned. The template is: "warn_log_embed"
*/
@Getter
@SuperBuilder
@Builder
@Setter
public class WarnContext extends SlimUserInitiatedServerContext {
public class WarnContext {
/**
* The reason why the warn was cast
*/
@@ -27,4 +29,8 @@ public class WarnContext extends SlimUserInitiatedServerContext {
*/
private Long warnId;
private Long infractionId;
private Member member;
private Guild guild;
private Message message;
private GuildMessageChannel channel;
}

View File

@@ -1,17 +1,14 @@
package dev.sheldan.abstracto.moderation.service;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import dev.sheldan.abstracto.moderation.model.BanResult;
import net.dv8tion.jda.api.entities.*;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
public interface BanService {
String BAN_EFFECT_KEY = "ban";
CompletableFuture<Void> banMemberWithNotification(Member member, String reason, Member banningMember, Integer deletionDays, Message message);
CompletableFuture<Void> banUserWithNotification(User user, String reason, Member banningMember, Integer deletionDays, Message message);
CompletableFuture<BanResult> banUserWithNotification(User user, String reason, Member banningMember, Integer deletionDays);
CompletableFuture<Void> unBanUserWithNotification(User user, Member unBanningUser);
CompletableFuture<Void> banUser(Guild guild, User user, Integer deletionDays, String reason);
CompletableFuture<Void> unbanUser(Guild guild, User user);

View File

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

View File

@@ -9,6 +9,7 @@ import java.util.List;
public interface UserNoteManagementService {
UserNote createUserNote(AUserInAServer aUserInAServer, String note);
void deleteNote(Long id, AServer server);
void deleteNote(UserNote userNote);
UserNote loadNote(Long serverId, Long userNoteId);
boolean noteExists(Long id, AServer server);
List<UserNote> loadNotesForUser(AUserInAServer aUserInAServer);

View File

@@ -5,22 +5,29 @@ 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.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition;
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
import dev.sheldan.abstracto.modmail.config.ModMailSlashCommandNames;
import dev.sheldan.abstracto.modmail.model.ClosingContext;
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
import dev.sheldan.abstracto.modmail.service.ModMailThreadService;
import dev.sheldan.abstracto.modmail.service.management.ModMailThreadManagementService;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
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;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -34,7 +41,12 @@ import java.util.concurrent.CompletableFuture;
@Slf4j
public class Close extends AbstractConditionableCommand {
public static final String MODMAIL_CLOSE_DEFAULT_NOTE_TEMPLATE_KEY = "modmail_close_default_note";
private static final String MODMAIL_CLOSE_DEFAULT_NOTE_TEMPLATE_KEY = "modmail_close_default_note";
private static final String CLOSE_COMMAND = "close";
private static final String NOTE_PARAMETER = "note";
private static final String SILENTLY_PARAMETER = "silently";
private static final String LOG_PARAMETER = "log";
private static final String CLOSE_RESPONSE = "close_response";
@Autowired
private ModMailContextCondition requiresModMailCondition;
@@ -50,6 +62,15 @@ public class Close extends AbstractConditionableCommand {
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private InteractionService interactionService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private Close self;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
@@ -61,6 +82,7 @@ public class Close extends AbstractConditionableCommand {
.builder()
.closingMember(commandContext.getAuthor())
.notifyUser(true)
.channel(commandContext.getChannel())
.log(true)
.note(note)
.build();
@@ -68,16 +90,96 @@ public class Close extends AbstractConditionableCommand {
.thenApply(aVoid -> CommandResult.fromIgnored());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String note;
if(slashCommandParameterService.hasCommandOption(NOTE_PARAMETER, event)) {
note = slashCommandParameterService.getCommandOption(NOTE_PARAMETER, event, String.class);
} else {
note = templateService.renderTemplate(MODMAIL_CLOSE_DEFAULT_NOTE_TEMPLATE_KEY, new Object(), event.getGuild().getIdLong());
}
Boolean silently;
if(slashCommandParameterService.hasCommandOption(SILENTLY_PARAMETER, event)) {
silently = slashCommandParameterService.getCommandOption(SILENTLY_PARAMETER, event, Boolean.class);
} else {
silently = false;
}
Boolean log;
if(slashCommandParameterService.hasCommandOption(LOG_PARAMETER, event)) {
log = slashCommandParameterService.getCommandOption(LOG_PARAMETER, event, Boolean.class);
} else {
log = false;
}
ClosingContext context = ClosingContext
.builder()
.closingMember(event.getMember())
.channel(event.getChannel())
.notifyUser(!silently)
.log(log)
.note(note)
.build();
return interactionService.replyEmbed(CLOSE_RESPONSE, event)
.thenCompose(interactionHook -> self.closeThread(context))
.thenApply(aVoid -> CommandResult.fromIgnored());
}
@Transactional
public CompletableFuture<Void> closeThread(ClosingContext closingContext) {
AChannel channel = channelManagementService.loadChannel(closingContext.getChannel());
ModMailThread thread = modMailThreadManagementService.getByChannel(channel);
return modMailThreadService.closeModMailThread(thread, closingContext, new ArrayList<>());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter note = Parameter.builder().name("note").type(String.class).remainder(true).optional(true).templated(true).build();
List<Parameter> parameters = Arrays.asList(note);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter noteParameter = Parameter
.builder()
.name(NOTE_PARAMETER)
.type(String.class)
.remainder(true)
.optional(true)
.templated(true)
.build();
Parameter silentlyParameter = Parameter
.builder()
.name(SILENTLY_PARAMETER)
.type(Boolean.class)
.remainder(true)
.slashCommandOnly(true)
.optional(true)
.templated(true)
.build();
Parameter logParameter = Parameter
.builder()
.name(LOG_PARAMETER)
.type(Boolean.class)
.remainder(true)
.slashCommandOnly(true)
.optional(true)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(noteParameter, silentlyParameter, logParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModMailSlashCommandNames.MODMAIL)
.commandName(CLOSE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("close")
.name(CLOSE_COMMAND)
.module(ModMailModuleDefinition.MODMAIL)
.parameters(parameters)
.help(helpInfo)
.slashCommandConfig(slashCommandConfig)
.async(true)
.supportsEmbedException(true)
.templated(true)

View File

@@ -4,16 +4,19 @@ 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.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
import dev.sheldan.abstracto.modmail.config.ModMailSlashCommandNames;
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
import dev.sheldan.abstracto.modmail.model.template.ModMailThreadExistsModel;
import dev.sheldan.abstracto.modmail.service.ModMailThreadService;
@@ -21,9 +24,13 @@ import dev.sheldan.abstracto.modmail.service.management.ModMailThreadManagementS
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
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;
@@ -36,6 +43,11 @@ import java.util.concurrent.CompletableFuture;
@Slf4j
public class Contact extends AbstractConditionableCommand {
private static final String CONTACT_PARAMETER = "contact";
private static final String USER_PARMETER = "user";
private static final String MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE = "modmail_thread_already_exists";
private static final String CONTACT_RESPONSE = "contact_response";
@Autowired
private ModMailThreadService modMailThreadService;
@@ -48,6 +60,12 @@ public class Contact extends AbstractConditionableCommand {
@Autowired
private ChannelService channelService;
@Autowired
private InteractionService interactionService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
Member targetUser = (Member) commandContext.getParameters().getParameters().get(0);
@@ -59,26 +77,73 @@ public class Contact extends AbstractConditionableCommand {
// containing a link to the channel, instead of opening a new one
if(modMailThreadManagementService.hasOpenModMailThreadForUser(user)) {
log.info("Modmail thread for user {} in server {} already exists. Notifying user {}.", commandContext.getAuthor().getId(), commandContext.getGuild().getId(), user.getUserReference().getId());
ModMailThreadExistsModel model = (ModMailThreadExistsModel) ContextConverter.fromCommandContext(commandContext, ModMailThreadExistsModel.class);
ModMailThread existingThread = modMailThreadManagementService.getOpenModMailThreadForUser(user);
model.setExistingModMailThread(existingThread);
List<CompletableFuture<Message>> futures = channelService.sendEmbedTemplateInTextChannelList("modmail_thread_already_exists", model, commandContext.getChannel());
ModMailThreadExistsModel model = ModMailThreadExistsModel
.builder()
.existingModMailThread(existingThread)
.build();
List<CompletableFuture<Message>> futures = channelService.sendEmbedTemplateInTextChannelList(MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE, model, commandContext.getChannel());
return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromIgnored());
} else {
return modMailThreadService.createModMailThreadForUser(targetUser, null, commandContext.getChannel(), false, commandContext.getUndoActions())
return modMailThreadService.createModMailThreadForUser(targetUser, null, false, commandContext.getUndoActions())
.thenCompose(unused -> modMailThreadService.sendContactNotification(targetUser, unused, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Member member = slashCommandParameterService.getCommandOption(USER_PARMETER, event, Member.class);
if(!member.getGuild().equals(event.getGuild())) {
throw new EntityGuildMismatchException();
}
AUserInAServer user = userManagementService.loadOrCreateUser(member);
// if this AUserInAServer already has an open thread, we should instead post a message
// containing a link to the channel, instead of opening a new one
if(modMailThreadManagementService.hasOpenModMailThreadForUser(user)) {
log.info("Modmail thread for user {} in server {} already exists. Notifying user {}.", event.getMember().getId(), event.getGuild().getId(), user.getUserReference().getId());
ModMailThread existingThread = modMailThreadManagementService.getOpenModMailThreadForUser(user);
ModMailThreadExistsModel model = ModMailThreadExistsModel
.builder()
.existingModMailThread(existingThread)
.build();
return interactionService.replyEmbed(MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE, model, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
} else {
CompletableFuture<InteractionHook> response = interactionService.replyEmbed(CONTACT_RESPONSE, event);
CompletableFuture<MessageChannel> threadFuture = modMailThreadService.createModMailThreadForUser(member, null, false, new ArrayList<>());
return CompletableFuture.allOf(response, threadFuture)
.thenCompose(unused -> modMailThreadService.sendContactNotification(member, threadFuture.join(), response.join()))
.thenApply(o -> CommandResult.fromSuccess());
}
}
@Override
public CommandConfiguration getConfiguration() {
Parameter responseText = Parameter.builder().name("user").type(Member.class).templated(true).build();
Parameter responseText = Parameter
.builder()
.name(USER_PARMETER)
.type(Member.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(responseText);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModMailSlashCommandNames.MODMAIL)
.commandName(CONTACT_PARAMETER)
.build();
return CommandConfiguration.builder()
.name("contact")
.name(CONTACT_PARAMETER)
.module(ModMailModuleDefinition.MODMAIL)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.async(true)
.help(helpInfo)
.supportsEmbedException(true)

View File

@@ -4,19 +4,24 @@ 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.HelpInfo;
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.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition;
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
import dev.sheldan.abstracto.modmail.config.ModMailSlashCommandNames;
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
import dev.sheldan.abstracto.modmail.service.ModMailSubscriptionService;
import dev.sheldan.abstracto.modmail.service.management.ModMailThreadManagementService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
@@ -26,6 +31,9 @@ import java.util.List;
@Component
public class Subscribe extends AbstractConditionableCommand {
private static final String SUBSCRIBE_COMMAND = "subscribe";
private static final String SUBSCRIBE_RESPONSE = "subscribe_response";
@Autowired
private ModMailContextCondition requiresModMailCondition;
@@ -38,6 +46,9 @@ public class Subscribe extends AbstractConditionableCommand {
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(commandContext.getChannel().getIdLong());
@@ -45,11 +56,31 @@ public class Subscribe extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(event.getChannel().getIdLong());
modMailSubscriptionService.subscribeToThread(userInServerManagementService.loadOrCreateUser(event.getMember()), modMailThread);
return interactionService.replyEmbed(SUBSCRIBE_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModMailSlashCommandNames.MODMAIL)
.commandName(SUBSCRIBE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("subscribe")
.name(SUBSCRIBE_COMMAND)
.slashCommandConfig(slashCommandConfig)
.module(ModMailModuleDefinition.MODMAIL)
.help(helpInfo)
.supportsEmbedException(true)

View File

@@ -4,20 +4,25 @@ 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.HelpInfo;
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.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition;
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
import dev.sheldan.abstracto.modmail.config.ModMailSlashCommandNames;
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
import dev.sheldan.abstracto.modmail.service.ModMailSubscriptionService;
import dev.sheldan.abstracto.modmail.service.management.ModMailThreadManagementService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
@@ -27,6 +32,9 @@ import java.util.List;
@Component
public class UnSubscribe extends AbstractConditionableCommand {
private static final String UN_SUBSCRIBE_COMMAND = "unSubscribe";
private static final String UN_SUBSCRIBE_RESPONSE = "unSubscribe_response";
@Autowired
private ModMailContextCondition requiresModMailCondition;
@@ -39,6 +47,9 @@ public class UnSubscribe extends AbstractConditionableCommand {
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(commandContext.getChannel().getIdLong());
@@ -47,11 +58,32 @@ public class UnSubscribe extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(event.getChannel().getIdLong());
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(event.getMember());
modMailSubscriptionService.unsubscribeFromThread(aUserInAServer, modMailThread);
return interactionService.replyEmbed(UN_SUBSCRIBE_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModMailSlashCommandNames.MODMAIL)
.commandName(UN_SUBSCRIBE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("unSubscribe")
.name(UN_SUBSCRIBE_COMMAND)
.slashCommandConfig(slashCommandConfig)
.module(ModMailModuleDefinition.MODMAIL)
.help(helpInfo)
.supportsEmbedException(true)

View File

@@ -8,7 +8,7 @@ import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition;
import dev.sheldan.abstracto.modmail.condition.detail.NotInModMailThreadConditionDetail;
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
import dev.sheldan.abstracto.modmail.service.management.ModMailThreadManagementService;
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;
@@ -24,9 +24,6 @@ public class RequiresModMailCondition implements ModMailContextCondition {
@Autowired
private ModMailThreadManagementService modMailThreadManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelManagementService channelManagementService;
@@ -34,8 +31,36 @@ public class RequiresModMailCondition implements ModMailContextCondition {
public ConditionResult shouldExecute(CommandContext commandContext, Command command) {
Optional<ModMailThread> threadOptional = modMailThreadManagementService.getByChannelOptional(channelManagementService.loadChannel(commandContext.getChannel()));
if(threadOptional.isPresent()) {
return ConditionResult.builder().result(true).build();
return ConditionResult
.builder()
.result(true)
.build();
}
return ConditionResult.builder().result(false).conditionDetail(new NotInModMailThreadConditionDetail()).build();
return ConditionResult
.builder()
.result(false)
.conditionDetail(new NotInModMailThreadConditionDetail())
.build();
}
@Override
public ConditionResult shouldExecute(SlashCommandInteractionEvent slashCommandInteractionEvent, Command command) {
Optional<ModMailThread> threadOptional = modMailThreadManagementService.getByChannelOptional(channelManagementService.loadChannel(slashCommandInteractionEvent.getChannel()));
if(threadOptional.isPresent()) {
return ConditionResult
.builder()
.result(true)
.build();
}
return ConditionResult
.builder()
.result(false)
.conditionDetail(new NotInModMailThreadConditionDetail())
.build();
}
@Override
public boolean supportsSlashCommands() {
return true;
}
}

View File

@@ -33,9 +33,6 @@ public class ModMailInitialButtonListener implements ButtonClickedListener {
@Autowired
private UndoActionService undoActionService;
@Autowired
private MessageService messageService;
@Autowired
private ComponentPayloadManagementService componentPayloadService;
@@ -58,7 +55,7 @@ public class ModMailInitialButtonListener implements ButtonClickedListener {
.thenCompose(member -> channelService.retrieveMessageInChannel(model.getEvent().getChannel(), choices.getMessageId())
.thenCompose(originalMessage -> {
try {
return modMailThreadService.createModMailThreadForUser(member, originalMessage, model.getEvent().getChannel(), true, undoActions);
return modMailThreadService.createModMailThreadForUser(member, originalMessage, true, undoActions);
} catch (Exception ex) {
log.error("Failed to setup thread correctly", ex);
undoActionService.performActions(undoActions);

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.modmail.service;
import dev.sheldan.abstracto.core.command.exception.AbstractoTemplatedException;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
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.metric.service.MetricTag;
@@ -39,6 +40,7 @@ import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.exceptions.InsufficientPermissionException;
import net.dv8tion.jda.api.interactions.InteractionHook;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -149,6 +151,9 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
@Autowired
private ComponentPayloadService componentPayloadService;
@Autowired
private InteractionService interactionService;
public static final String MODMAIL_THREAD_METRIC = "modmail.threads";
public static final String MODMAIL_MESSAGE_METRIC = "modmail.messges";
public static final String ACTION = "action";
@@ -180,7 +185,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
public static final String MODMAIL_INITIAL_ORIGIN = "modmailInitial";
@Override
public CompletableFuture<Void> createModMailThreadForUser(Member member, Message initialMessage, MessageChannel feedBackChannel, boolean userInitiated, List<UndoActionInstance> undoActions) {
public CompletableFuture<MessageChannel> createModMailThreadForUser(Member member, Message initialMessage, boolean userInitiated, List<UndoActionInstance> undoActions) {
Long serverId = member.getGuild().getIdLong();
Long categoryId = configService.getLongValue(MODMAIL_CATEGORY, serverId);
AServer server = serverManagementService.loadServer(member.getGuild().getIdLong());
@@ -199,20 +204,32 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
CompletableFuture<TextChannel> textChannelFuture = channelService.createTextChannel(channelName, server, categoryId);
return textChannelFuture.thenCompose(channel -> {
undoActions.add(UndoActionInstance.getChannelDeleteAction(serverId, channel.getIdLong()));
return self.performModMailThreadSetup(member, initialMessage, channel, userInitiated, undoActions, feedBackChannel);
return self.performModMailThreadSetup(member, initialMessage, channel, userInitiated, undoActions)
.thenCompose(unused -> CompletableFuture.completedFuture(channel));
});
}
@Transactional
public CompletableFuture<Void> sendContactNotification(Member member, TextChannel textChannel, MessageChannel feedBackChannel) {
@Override
public CompletableFuture<Void> sendContactNotification(Member member, MessageChannel messageChannel, MessageChannel feedBackChannel) {
ContactNotificationModel model = ContactNotificationModel
.builder()
.createdChannel(textChannel)
.createdChannel(messageChannel)
.targetMember(member)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannelList(MODMAIL_THREAD_CREATED_TEMPLATE_KEY, model, feedBackChannel));
}
@Override
public CompletableFuture<Void> sendContactNotification(Member member, MessageChannel createdMessageChannel, InteractionHook interactionHook) {
ContactNotificationModel model = ContactNotificationModel
.builder()
.createdChannel(createdMessageChannel)
.targetMember(member)
.build();
return FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(MODMAIL_THREAD_CREATED_TEMPLATE_KEY, model, interactionHook));
}
/**
* This method is responsible for creating the instance in the database, sending the header in the newly created text channel and forwarding the initial message
* by the user (if any), after this is complete, this method executes the method to perform the mod mail notification.
@@ -224,7 +241,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
* @return A {@link CompletableFuture future} which completes when the setup is done
*/
@Transactional
public CompletableFuture<Void> performModMailThreadSetup(Member member, Message initialMessage, TextChannel channel, boolean userInitiated, List<UndoActionInstance> undoActions, MessageChannel feedBackChannel) {
public CompletableFuture<Void> performModMailThreadSetup(Member member, Message initialMessage, TextChannel channel, boolean userInitiated, List<UndoActionInstance> undoActions) {
log.info("Performing modmail thread setup for channel {} for user {} in server {}. It was initiated by a user: {}.", channel.getIdLong(), member.getId(), channel.getGuild().getId(), userInitiated);
CompletableFuture<Void> headerFuture = sendModMailHeader(channel, member);
CompletableFuture<Message> userReplyMessage;
@@ -244,10 +261,6 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
return CompletableFuture.allOf(headerFuture, notificationFuture, userReplyMessage).thenAccept(aVoid -> {
undoActions.clear();
self.setupModMailThreadInDB(initialMessage, channel, member, userReplyMessage.join());
}).thenAccept(unused -> {
if(!userInitiated) {
self.sendContactNotification(member, channel, feedBackChannel);
}
});
}
@@ -368,7 +381,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
log.info("Only one server available to modmail. Directly opening modmail thread for user {} in server {}.", initialMessage.getAuthor().getId(), chosenServerId);
memberService.getMemberInServerAsync(chosenServerId, initialMessage.getAuthor().getIdLong()).thenCompose(member -> {
try {
return self.createModMailThreadForUser(member, initialMessage, initialMessage.getChannel(), true, undoActions);
return self.createModMailThreadForUser(member, initialMessage, true, undoActions).thenApply(messageChannel -> null);
} catch (Exception exception) {
CompletableFuture<Void> future = new CompletableFuture<>();
future.completeExceptionally(exception);

View File

@@ -0,0 +1,5 @@
package dev.sheldan.abstracto.modmail.config;
public class ModMailSlashCommandNames {
public static final String MODMAIL = "modmail";
}

View File

@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.modmail.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Channel;
import net.dv8tion.jda.api.entities.Member;
@Getter
@@ -12,5 +13,6 @@ public class ClosingContext {
private Boolean notifyUser;
private Boolean log;
private Member closingMember;
private Channel channel;
private String note;
}

View File

@@ -4,12 +4,12 @@ import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.MessageChannel;
@Getter
@Setter
@Builder
public class ContactNotificationModel {
private Member targetMember;
private TextChannel createdChannel;
private MessageChannel createdChannel;
}

View File

@@ -1,11 +1,10 @@
package dev.sheldan.abstracto.modmail.model.template;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import dev.sheldan.abstracto.core.utils.ChannelUtils;
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
/**
* This model is used to notify a staff member that there is already a mod mail thread open for the user
@@ -13,8 +12,8 @@ import lombok.experimental.SuperBuilder;
*/
@Getter
@Setter
@SuperBuilder
public class ModMailThreadExistsModel extends UserInitiatedServerContext {
@Builder
public class ModMailThreadExistsModel {
private ModMailThread existingModMailThread;
public String getThreadUrl() {

View File

@@ -7,10 +7,8 @@ import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.modmail.model.ClosingContext;
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
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.MessageChannel;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.interactions.InteractionHook;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -25,12 +23,14 @@ public interface ModMailThreadService {
* the necessary data in the database, notifying the users and sending messages related to the creation of the {@link ModMailThread}
* @param member The {@link AUserInAServer} to create the mod mail thread for
* @param initialMessage The initial message sparking this mod mail thread, null in case it was created by a command
* @param feedBackChannel The {@link MessageChannel} in which feedback about exceptions should be posted to
* @param userInitiated Whether or not the mod mail thread was initiated by a user
* @param undoActions A list of {@link dev.sheldan.abstracto.core.models.UndoAction actions} to be undone in case the operation fails. This list will be filled in the method.
* @return A {@link CompletableFuture future} which completes when the modmail thread is set up
*/
CompletableFuture<Void> createModMailThreadForUser(Member member, Message initialMessage, MessageChannel feedBackChannel, boolean userInitiated, List<UndoActionInstance> undoActions);
CompletableFuture<MessageChannel> createModMailThreadForUser(Member member, Message initialMessage, boolean userInitiated, List<UndoActionInstance> undoActions);
CompletableFuture<Void> sendContactNotification(Member member, MessageChannel createdMessageChannel, MessageChannel feedBackChannel);
CompletableFuture<Void> sendContactNotification(Member member, MessageChannel createdMessageChannel, InteractionHook interactionHook);
/**
* Changes the configuration value of the category used to create mod mail threads to the given ID.

View File

@@ -5,24 +5,32 @@ 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.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.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.core.utils.ParseUtils;
import dev.sheldan.abstracto.core.utils.SnowflakeUtils;
import dev.sheldan.abstracto.remind.config.RemindFeatureDefinition;
import dev.sheldan.abstracto.remind.config.RemindSlashCommandNames;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import dev.sheldan.abstracto.remind.model.template.commands.ReminderModel;
import dev.sheldan.abstracto.remind.service.ReminderService;
import lombok.extern.slf4j.Slf4j;
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;
@@ -31,44 +39,107 @@ import java.util.concurrent.CompletableFuture;
public class Remind extends AbstractConditionableCommand {
public static final String REMINDER_EMBED_KEY = "remind_response";
public static final String DURATION_PARAMETER = "duration";
public static final String REMIND_TEXT_PARAMETER = "remindText";
@Autowired
private ReminderService remindService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Duration remindTime = (Duration) parameters.get(0);
String text = (String) parameters.get(1);
Long serverId = commandContext.getGuild().getIdLong();
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
ReminderModel remindModel = (ReminderModel) ContextConverter.fromCommandContext(commandContext, ReminderModel.class);
remindModel.setRemindText(text);
Reminder createdReminder = remindService.createReminderInForUser(aUserInAServer, text, remindTime, commandContext.getMessage());
ReminderModel remindModel = ReminderModel
.builder()
.remindText(text)
.member(commandContext.getAuthor())
.reminder(createdReminder)
.build();
remindModel.setReminder(createdReminder);
log.info("Notifying user {} about reminder being scheduled.", commandContext.getAuthor().getId());
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(REMINDER_EMBED_KEY, remindModel, commandContext.getChannel()))
MessageToSend messageToSend = templateService.renderEmbedTemplate(REMINDER_EMBED_KEY, remindModel, serverId);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String durationString = slashCommandParameterService.getCommandOption(DURATION_PARAMETER, event, Duration.class, String.class);
Duration duration = ParseUtils.parseDuration(durationString);
String reminderText = slashCommandParameterService.getCommandOption(REMIND_TEXT_PARAMETER, event, String.class, String.class);
Long serverId = event.getGuild().getIdLong();
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(event.getMember());
Long snowFlake = SnowflakeUtils.createSnowFlake();
Reminder createdReminder = remindService.createReminderInForUser(aUserInAServer, reminderText, duration, event.getChannel().getIdLong(), snowFlake);
ReminderModel remindModel = ReminderModel
.builder()
.remindText(reminderText)
.member(event.getMember())
.reminder(createdReminder)
.build();
remindModel.setReminder(createdReminder);
log.info("Notifying user {} about reminder being scheduled.", event.getMember().getId());
MessageToSend messageToSend = templateService.renderEmbedTemplate(REMINDER_EMBED_KEY, remindModel, serverId);
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("duration").type(Duration.class).templated(true).build());
parameters.add(Parameter.builder().name("remindText").type(String.class).templated(true).remainder(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
Parameter durationParameter = Parameter
.builder()
.name(DURATION_PARAMETER)
.type(Duration.class)
.templated(true)
.build();
Parameter remindTextParameter = Parameter
.builder()
.name(REMIND_TEXT_PARAMETER)
.type(String.class)
.templated(true)
.remainder(true)
.build();
List<Parameter> parameters = Arrays.asList(durationParameter, remindTextParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.hasExample(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(RemindSlashCommandNames.REMIND)
.commandName("create")
.build();
return CommandConfiguration.builder()
.name("remind")
.async(true)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)

View File

@@ -4,26 +4,33 @@ import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
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.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.ServerChannelMessage;
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.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.remind.config.RemindFeatureDefinition;
import dev.sheldan.abstracto.remind.config.RemindSlashCommandNames;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import dev.sheldan.abstracto.remind.model.template.commands.ReminderDisplay;
import dev.sheldan.abstracto.remind.model.template.commands.RemindersModel;
import dev.sheldan.abstracto.remind.service.management.ReminderManagementService;
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.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
@Slf4j
@@ -39,38 +46,75 @@ public class Reminders extends AbstractConditionableCommand {
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
Long serverId = commandContext.getGuild().getIdLong();
Member member = commandContext.getAuthor();
MessageToSend messageToSend = getMessageToSend(serverId, member);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
}
private MessageToSend getMessageToSend(Long serverId, Member member) {
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
List<Reminder> activeReminders = reminderManagementService.getActiveRemindersForUser(aUserInAServer);
RemindersModel model = (RemindersModel) ContextConverter.fromCommandContext(commandContext, RemindersModel.class);
activeReminders.forEach(reminder -> {
List<ReminderDisplay> reminders = activeReminders.stream().map(reminder -> {
ServerChannelMessage originMessage = ServerChannelMessage
.builder()
.messageId(reminder.getMessageId())
.channelId(reminder.getChannel().getId())
.serverId(commandContext.getGuild().getIdLong())
.serverId(serverId)
.build();
ReminderDisplay display = ReminderDisplay
return ReminderDisplay
.builder()
.reminder(reminder)
.message(originMessage)
.build();
model.getReminders().add(display);
});
log.info("Showing {} reminders for user {} in server {}.", activeReminders.size(), commandContext.getAuthor().getId(), commandContext.getGuild().getId());
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(REMINDERS_RESPONSE_TEMPLATE, model, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
}).collect(Collectors.toList());
RemindersModel model = RemindersModel
.builder()
.reminders(reminders)
.member(member)
.build();
log.info("Showing {} reminders for user {} in server {}.", activeReminders.size(), aUserInAServer.getUserReference().getId(), serverId);
return templateService.renderEmbedTemplate(REMINDERS_RESPONSE_TEMPLATE, model, serverId);
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long serverId = event.getGuild().getIdLong();
Member member = event.getMember();
MessageToSend messageToSend = getMessageToSend(serverId, member);
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(RemindSlashCommandNames.REMIND)
.commandName("list")
.build();
return CommandConfiguration.builder()
.name("reminders")
.async(true)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(true)
.help(helpInfo)

View File

@@ -5,29 +5,47 @@ 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.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.ParseUtils;
import dev.sheldan.abstracto.remind.config.RemindFeatureDefinition;
import dev.sheldan.abstracto.remind.config.RemindSlashCommandNames;
import dev.sheldan.abstracto.remind.service.ReminderService;
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 Snooze extends AbstractConditionableCommand {
private static final String SNOOZE_COMMAND = "snooze";
private static final String DURATION_PARAMETER = "duration";
private static final String REMINDER_ID_PARAMETER = "reminderId";
private static final String SNOOZE_RESPONSE = "snooze_response";
@Autowired
private ReminderService reminderService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private InteractionService interactionService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> newParameters = commandContext.getParameters().getParameters();
@@ -38,18 +56,51 @@ public class Snooze extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long reminderId = slashCommandParameterService.getCommandOption(REMINDER_ID_PARAMETER, event, Long.class, Integer.class).longValue();
String newDurationString = slashCommandParameterService.getCommandOption(DURATION_PARAMETER, event, String.class, String.class);
Duration newDuration = ParseUtils.parseDuration(newDurationString);
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(event.getMember());
reminderService.snoozeReminder(reminderId, aUserInAServer, newDuration);
return interactionService.replyEmbed(SNOOZE_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("reminderId").type(Long.class).templated(true).build());
parameters.add(Parameter.builder().name("duration").type(Duration.class).templated(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter reminderParameter = Parameter
.builder()
.name(REMINDER_ID_PARAMETER)
.type(Long.class)
.templated(true)
.build();
Parameter durationParameter = Parameter
.builder()
.name(DURATION_PARAMETER)
.type(Duration.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(reminderParameter, durationParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(RemindSlashCommandNames.REMIND)
.commandName(SNOOZE_COMMAND)
.build();
return CommandConfiguration.builder()
.name("snooze")
.name(SNOOZE_COMMAND)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.supportsEmbedException(true)
.causesReaction(true)
.slashCommandConfig(slashCommandConfig)
.parameters(parameters)
.help(helpInfo)
.build();

View File

@@ -2,35 +2,45 @@ package dev.sheldan.abstracto.remind.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
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.validator.MinIntegerValueValidator;
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.management.UserInServerManagementService;
import dev.sheldan.abstracto.remind.config.RemindFeatureDefinition;
import dev.sheldan.abstracto.remind.config.RemindSlashCommandNames;
import dev.sheldan.abstracto.remind.service.ReminderService;
import lombok.extern.slf4j.Slf4j;
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;
@Slf4j
@Component
public class UnRemind extends AbstractConditionableCommand {
private static final String UN_REMIND_COMMAND = "unRemind";
private static final String REMINDER_ID_PARAMETER = "reminderId";
private static final String UN_REMIND_RESPONSE = "unRemind_response";
@Autowired
private ReminderService reminderService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CommandResult execute(CommandContext commandContext) {
Long reminderId = (Long) commandContext.getParameters().getParameters().get(0);
@@ -38,17 +48,41 @@ public class UnRemind extends AbstractConditionableCommand {
return CommandResult.fromSuccess();
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long reminderId = slashCommandParameterService.getCommandOption(REMINDER_ID_PARAMETER, event, Long.class, Integer.class).longValue();
reminderService.unRemind(reminderId, userInServerManagementService.loadOrCreateUser(event.getMember()));
return interactionService.replyEmbed(UN_REMIND_RESPONSE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
List<ParameterValidator> reminderIdValidator = Arrays.asList(MinIntegerValueValidator.min(1L));
parameters.add(Parameter.builder().name("reminderId").validators(reminderIdValidator).templated(true).type(Long.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter reminderParameter = Parameter
.builder()
.name(REMINDER_ID_PARAMETER)
.templated(true)
.type(Long.class)
.build();
List<Parameter> parameters = Arrays.asList(reminderParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(RemindSlashCommandNames.REMIND)
.commandName("cancel")
.build();
return CommandConfiguration.builder()
.name("unRemind")
.name(UN_REMIND_COMMAND)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.supportsEmbedException(true)
.slashCommandConfig(slashCommandConfig)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -92,6 +92,11 @@ public class RemindServiceBean implements ReminderService {
return reminder;
}
@Override
public Reminder createReminderInForUser(AUserInAServer user, String remindText, Duration remindIn, Long channelId) {
return createReminderInForUser(user, remindText, remindIn, channelId, null);
}
private void scheduleReminder(Duration remindIn, Reminder reminder) {
if(remindIn.getSeconds() < 60) {
reminder.setJobTriggerKey(null);

View File

@@ -38,6 +38,11 @@ public class ReminderManagementServiceBean implements ReminderManagementService
return reminderRepository.save(reminder);
}
@Override
public Reminder createReminder(AServerAChannelAUser userToBeReminded, String text, Instant timeToBeRemindedAt) {
return null;
}
@Override
public Optional<Reminder> loadReminderOptional(Long reminderId) {
return reminderRepository.findById(reminderId);

View File

@@ -1,63 +0,0 @@
package dev.sheldan.abstracto.remind.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
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.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.remind.model.template.commands.ReminderModel;
import dev.sheldan.abstracto.remind.service.ReminderService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class RemindTest {
@InjectMocks
private Remind testUnit;
@Mock
private ReminderService remindService;
@Mock
private ChannelService channelService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Captor
private ArgumentCaptor<ReminderModel> captor;
@Test
public void executeCommand() {
String reminderText = "text";
Duration duration = Duration.ofMinutes(10);
CommandContext withParameters = CommandTestUtilities.getWithParameters(Arrays.asList(duration, reminderText));
AUserInAServer user = Mockito.mock(AUserInAServer.class);
when(userInServerManagementService.loadOrCreateUser(withParameters.getAuthor())).thenReturn(user);
CompletableFuture<CommandResult> result = testUnit.executeAsync(withParameters);
verify(remindService, times(1)).createReminderInForUser(user, reminderText, duration, withParameters.getMessage());
verify(channelService, times(1)).sendEmbedTemplateInTextChannelList(eq(Remind.REMINDER_EMBED_KEY), captor.capture(), eq(withParameters.getChannel()));
ReminderModel reminderModel = captor.getValue();
Assert.assertEquals(reminderText, reminderModel.getRemindText());
Assert.assertEquals(withParameters.getMessage(), reminderModel.getMessage());
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -1,72 +0,0 @@
package dev.sheldan.abstracto.remind.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
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.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import dev.sheldan.abstracto.remind.model.template.commands.RemindersModel;
import dev.sheldan.abstracto.remind.service.management.ReminderManagementService;
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.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class RemindersTest {
@InjectMocks
private Reminders testUnit;
@Mock
private ReminderManagementService reminderManagementService;
@Mock
private ChannelService channelService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private AChannel channel;
@Captor
private ArgumentCaptor<RemindersModel> modelCaptor;
@Test
public void testExecuteCommand() {
CommandContext context = CommandTestUtilities.getNoParameters();
Reminder reminder = Mockito.mock(Reminder.class);
when(reminder.getChannel()).thenReturn(channel);
Reminder secondReminder = Mockito.mock(Reminder.class);
when(secondReminder.getChannel()).thenReturn(channel);
List<Reminder> reminders = Arrays.asList(reminder, secondReminder);
AUserInAServer user = Mockito.mock(AUserInAServer.class);
when(userInServerManagementService.loadOrCreateUser(context.getAuthor())).thenReturn(user);
when(reminderManagementService.getActiveRemindersForUser(user)).thenReturn(reminders);
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
verify(channelService, times(1)).sendEmbedTemplateInTextChannelList(eq(Reminders.REMINDERS_RESPONSE_TEMPLATE), modelCaptor.capture(), eq(context.getChannel()));
RemindersModel usedModel = modelCaptor.getValue();
Assert.assertEquals(reminder, usedModel.getReminders().get(0).getReminder());
Assert.assertEquals(secondReminder, usedModel.getReminders().get(1).getReminder());
Assert.assertEquals(reminders.size(), usedModel.getReminders().size());
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -0,0 +1,5 @@
package dev.sheldan.abstracto.remind.config;
public class RemindSlashCommandNames {
public static final String REMIND = "remind";
}

View File

@@ -1,15 +1,16 @@
package dev.sheldan.abstracto.remind.model.template.commands;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
@Getter
@Setter
@SuperBuilder
public class ReminderModel extends UserInitiatedServerContext {
@Builder
public class ReminderModel {
private String remindText;
private Member member;
private Reminder reminder;
}

View File

@@ -1,18 +1,18 @@
package dev.sheldan.abstracto.remind.model.template.commands;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@SuperBuilder
public class RemindersModel extends UserInitiatedServerContext {
@Builder
public class RemindersModel {
@Builder.Default
private List<ReminderDisplay> reminders = new ArrayList<>();
private Member member;
}

View File

@@ -9,6 +9,7 @@ import java.time.Duration;
public interface ReminderService {
Reminder createReminderInForUser(AUserInAServer user, String remindText, Duration remindIn, Message message);
Reminder createReminderInForUser(AUserInAServer user, String remindText, Duration remindIn, Long channelId, Long messageId);
Reminder createReminderInForUser(AUserInAServer user, String remindText, Duration remindIn, Long channelId);
void executeReminder(Long reminderId);
void unRemind(Long reminderId, AUserInAServer userInAServer);
void snoozeReminder(Long reminderId, AUserInAServer user, Duration newDuration);

View File

@@ -10,6 +10,7 @@ import java.util.Optional;
public interface ReminderManagementService {
Reminder createReminder(AServerAChannelAUser userToBeReminded, String text, Instant timeToBeRemindedAt, Long messageId);
Reminder createReminder(AServerAChannelAUser userToBeReminded, String text, Instant timeToBeRemindedAt);
Optional<Reminder> loadReminderOptional(Long reminderId);
Reminder loadReminder(Long reminderId);
void setReminded(Reminder reminder);

View File

@@ -5,15 +5,22 @@ 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.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelService;
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.starboard.config.StarboardFeatureDefinition;
import dev.sheldan.abstracto.starboard.config.StarboardSlashCommandNames;
import dev.sheldan.abstracto.starboard.model.template.MemberStarStatsModel;
import dev.sheldan.abstracto.starboard.service.StarboardService;
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;
@@ -26,6 +33,8 @@ public class StarStats extends AbstractConditionableCommand {
public static final String STARSTATS_RESPONSE_TEMPLATE = "starStats_response";
public static final String STARSTATS_SINGLE_MEMBER_RESPONSE_TEMPLATE = "starStats_single_member_response";
private static final String STAR_STATS_COMMAND = "starStats";
private static final String MEMBER_PARAMETER = "member";
@Autowired
private StarboardService starboardService;
@@ -33,31 +42,77 @@ public class StarStats extends AbstractConditionableCommand {
@Autowired
private ChannelService channelService;
@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();
if(parameters.isEmpty()) {
return starboardService.retrieveStarStats(commandContext.getGuild().getIdLong())
.thenCompose(starStatsModel ->
FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(STARSTATS_RESPONSE_TEMPLATE, starStatsModel, commandContext.getChannel()))
).thenApply(o -> CommandResult.fromIgnored());
.thenCompose(starStatsModel -> {
MessageToSend messageToSend = templateService.renderEmbedTemplate(STARSTATS_RESPONSE_TEMPLATE, starStatsModel, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()));
}).thenApply(o -> CommandResult.fromIgnored());
} else {
Member targetMember = (Member) parameters.get(0);
MemberStarStatsModel memberStarStatsModel = starboardService.retrieveStarStatsForMember(targetMember);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(STARSTATS_SINGLE_MEMBER_RESPONSE_TEMPLATE, memberStarStatsModel, commandContext.getChannel()))
MessageToSend messageToSend = templateService.renderEmbedTemplate(STARSTATS_SINGLE_MEMBER_RESPONSE_TEMPLATE, memberStarStatsModel, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
if(slashCommandParameterService.hasCommandOption(MEMBER_PARAMETER, event)) {
Member targetMember = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER, event, Member.class, Member.class);
MemberStarStatsModel memberStarStatsModel = starboardService.retrieveStarStatsForMember(targetMember);
MessageToSend messageToSend = templateService.renderEmbedTemplate(STARSTATS_SINGLE_MEMBER_RESPONSE_TEMPLATE, memberStarStatsModel, event.getGuild().getIdLong());
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
} else {
return starboardService.retrieveStarStats(event.getGuild().getIdLong())
.thenCompose(starStatsModel -> {
MessageToSend messageToSend = templateService.renderEmbedTemplate(STARSTATS_RESPONSE_TEMPLATE, starStatsModel, event.getGuild().getIdLong());
return interactionService.replyMessageToSend(messageToSend, event);
}).thenApply(o -> CommandResult.fromIgnored());
}
}
@Override
public CommandConfiguration getConfiguration() {
Parameter memberParameter = Parameter.builder().templated(true).name("member").type(Member.class).optional(true).build();
Parameter memberParameter = Parameter
.builder()
.templated(true)
.name(MEMBER_PARAMETER)
.type(Member.class)
.optional(true)
.build();
List<Parameter> parameters = Collections.singletonList(memberParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(StarboardSlashCommandNames.STARBOARD)
.commandName(STAR_STATS_COMMAND)
.build();
return CommandConfiguration.builder()
.name("starStats")
.name(STAR_STATS_COMMAND)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.supportsEmbedException(true)
.causesReaction(false)

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