Compare commits

..

26 Commits

Author SHA1 Message Date
Sheldan
af5f9a8361 [maven-release-plugin] prepare release v1.5.13 2023-12-10 14:32:05 +01:00
Sheldan
e84b5ecbb5 [AB-30] adding giveaway feature 2023-12-10 14:25:08 +01:00
Sheldan
1ba8219d7b [AB-xxx] fixing using wrong parameter for unSuggest command 2023-11-15 01:06:30 +01:00
Sheldan
60d2c669a2 [maven-release-plugin] prepare for next development iteration 2023-10-29 18:23:09 +01:00
Sheldan
3a122a72e5 [maven-release-plugin] prepare release v1.5.12 2023-10-29 18:23:03 +01:00
Sheldan
d53126e5a5 [AB-xxx] actively limiting auto complete responses to the max allowed values
changing interface of slash command parameter service to be more applicable
adding utility functions to slash command auto complete service bean
2023-10-29 18:21:29 +01:00
Sheldan
208d9a28ed [maven-release-plugin] prepare for next development iteration 2023-10-23 01:27:29 +02:00
Sheldan
6643359953 [maven-release-plugin] prepare release v1.5.11 2023-10-23 01:27:22 +02:00
Sheldan
bd7149d159 [AB-xxx] prepare for release 2023-10-23 01:26:12 +02:00
Sheldan
c0b6a19bca [AB-82] adding instant for cooldown exception model
removing not needed models for payday and slots cooldown exceptions
2023-10-04 23:34:16 +02:00
Sheldan
58eece0814 [AB-xxx] setting duration to zero if it was not provided, for infraction storage 2023-10-04 22:45:51 +02:00
Sheldan
abfd2c9591 [AB-xxx] ignore slash command only commands for message commands 2023-09-26 23:37:04 +02:00
Sheldan
a71a448b4b [maven-release-plugin] prepare for next development iteration 2023-09-26 22:51:02 +02:00
Sheldan
4d1ce7158f [maven-release-plugin] prepare release v1.5.10 2023-09-26 22:50:58 +02:00
Sheldan
9bbb9430d9 [AB-xxx] preparing for release 2023-09-26 22:49:00 +02:00
Sheldan
a608fba082 [AB-106] renaming deletion days to deletion duration 2023-09-26 00:58:01 +02:00
Sheldan
f97fe42e65 [AB-106] adding ability to specify deletion duration on ban command
removing banDelete command
updating jda version
2023-09-26 00:56:15 +02:00
Sheldan
f12581f322 [AB-xxx] adding information whether a command is slash/message command only to help output
resizing template content column
2023-09-20 02:01:09 +02:00
Sheldan
e8f630c94c [AB-xxx] never using the interaction in echo when a channel is provided 2023-09-09 23:58:31 +02:00
Sheldan
14f35caede [AB-xxx] never using the interaction in echo when a channel is provided 2023-09-09 23:57:23 +02:00
Sheldan
915eb8ced9 [AB-xxx] fixing not limiting custom commands for auto complete 2023-09-08 00:17:43 +02:00
Sheldan
148c10f250 [maven-release-plugin] prepare for next development iteration 2023-09-07 23:30:10 +02:00
Sheldan
74e5154e29 [maven-release-plugin] prepare release v1.5.9 2023-09-07 23:30:05 +02:00
Sheldan
baad696a20 [AB-xxx] adding auto complete to custom command get 2023-09-07 22:57:51 +02:00
Sheldan
b7b5dc7b6a [AB-xxx] fixing installer version 2023-09-07 21:11:22 +02:00
Sheldan
f27f59c0b3 [maven-release-plugin] prepare for next development iteration 2023-09-07 21:10:31 +02:00
240 changed files with 4150 additions and 412 deletions

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

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

View File

@@ -41,13 +41,19 @@ public class ActivateAssignableRolePlace extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
Parameter placeName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter placeName = Parameter
.builder()
.name("name")
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(placeName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("activateAssignableRolePlace")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.messageCommandOnly(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)

View File

@@ -36,16 +36,40 @@ public class AddAssignableRoleCondition extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
Parameter placeName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter role = Parameter.builder().name("role").type(Role.class).templated(true).build();
Parameter conditionKey = Parameter.builder().name("conditionKey").type(AssignableRoleConditionType.class).templated(true).build();
Parameter conditionValue = Parameter.builder().name("conditionParameter").type(String.class).templated(true).build();
Parameter placeName = Parameter
.builder()
.name("name")
.type(String.class)
.templated(true)
.build();
Parameter role = Parameter
.builder()
.name("role")
.type(Role.class)
.templated(true)
.build();
Parameter conditionKey = Parameter
.builder()
.name("conditionKey")
.type(AssignableRoleConditionType.class)
.templated(true)
.build();
Parameter conditionValue = Parameter
.builder()
.name("conditionParameter")
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(placeName, role, conditionKey, conditionValue);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("addAssignableRoleCondition")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.messageCommandOnly(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -62,17 +62,42 @@ public class AddRoleToAssignableRolePost extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
Parameter placeName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter role = Parameter.builder().name("role").type(Role.class).templated(true).build();
Parameter rolePostName = Parameter.builder().name("displayText").type(String.class).templated(true).build();
Parameter emote = Parameter.builder().name("emote").type(FullEmote.class).optional(true).templated(true).build();
Parameter placeName = Parameter
.builder()
.name("name")
.type(String.class)
.templated(true)
.build();
Parameter role = Parameter
.builder()
.name("role")
.type(Role.class)
.templated(true)
.build();
Parameter rolePostName = Parameter
.builder()
.name("displayText")
.type(String.class)
.templated(true)
.build();
Parameter emote = Parameter
.builder()
.name("emote")
.type(FullEmote.class)
.optional(true)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(placeName, role, rolePostName, emote);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("addRoleToAssignableRolePlace")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.messageCommandOnly(true)
.async(true)
.supportsEmbedException(true)
.parameters(parameters)

View File

@@ -47,9 +47,24 @@ public class ChangeAssignableRolePlaceConfig extends AbstractConditionableComman
@Override
public CommandConfiguration getConfiguration() {
Parameter assignableRolePlaceName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter parameterKey = Parameter.builder().name("key").type(AssignableRolePlaceParameterKey.class).templated(true).build();
Parameter parameterValue = Parameter.builder().name("value").type(String.class).templated(true).build();
Parameter assignableRolePlaceName = Parameter
.builder()
.name("name")
.type(String.class)
.templated(true)
.build();
Parameter parameterKey = Parameter
.builder()
.name("key")
.type(AssignableRolePlaceParameterKey.class)
.templated(true)
.build();
Parameter parameterValue = Parameter
.builder()
.name("value")
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(assignableRolePlaceName, parameterKey, parameterValue);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
@@ -57,6 +72,7 @@ public class ChangeAssignableRolePlaceConfig extends AbstractConditionableComman
.name("changeAssignableRolePlaceConfig")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.messageCommandOnly(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)

View File

@@ -33,9 +33,6 @@ public class CreateAssignableRolePost extends AbstractConditionableCommand {
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
@@ -57,20 +54,47 @@ public class CreateAssignableRolePost extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<ParameterValidator> rolePlaceNameValidator = Arrays.asList(MaxStringLengthValidator.max(AssignableRolePlace.ASSIGNABLE_PLACE_NAME_LIMIT));
Parameter rolePostName = Parameter.builder().name("name").validators(rolePlaceNameValidator).type(String.class).templated(true).build();
Parameter channel = Parameter.builder().name("channel").type(TextChannel.class).templated(true).build();
Parameter type = Parameter.builder().name("type").type(AssignableRolePlaceType.class).templated(true).optional(true).build();
Parameter rolePostName = Parameter
.builder()
.name("name")
.validators(rolePlaceNameValidator)
.type(String.class)
.templated(true)
.build();
Parameter channel = Parameter
.builder()
.name("channel")
.type(TextChannel.class)
.templated(true)
.build();
Parameter type = Parameter
.builder()
.name("type")
.type(AssignableRolePlaceType.class)
.templated(true)
.optional(true)
.build();
List<ParameterValidator> rolePlaceDescriptionValidator = Arrays.asList(MaxStringLengthValidator.max(AssignableRolePlace.ASSIGNABLE_PLACE_NAME_LIMIT));
Parameter text = Parameter.builder().name("text").validators(rolePlaceDescriptionValidator).type(String.class).templated(true).build();
Parameter text = Parameter
.builder()
.name("text")
.validators(rolePlaceDescriptionValidator)
.type(String.class)
.templated(true)
.build();
List<String> aliases = Arrays.asList("crRPl", "crAssRoPl");
List<Parameter> parameters = Arrays.asList(rolePostName, channel, text, type);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("createAssignableRolePlace")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.supportsEmbedException(true)
.causesReaction(true)
.messageCommandOnly(true)
.parameters(parameters)
.aliases(aliases)
.help(helpInfo)

View File

@@ -41,7 +41,12 @@ public class DeactivateAssignableRolePlace extends AbstractConditionableCommand
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter rolePostName = Parameter
.builder()
.name("name")
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(rolePostName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
@@ -49,6 +54,7 @@ public class DeactivateAssignableRolePlace extends AbstractConditionableCommand
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.async(true)
.messageCommandOnly(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -41,7 +41,12 @@ public class DeleteAssignableRolePlace extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter rolePostName = Parameter
.builder()
.name("name")
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(rolePostName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
@@ -50,6 +55,7 @@ public class DeleteAssignableRolePlace extends AbstractConditionableCommand {
.templated(true)
.causesReaction(true)
.async(true)
.messageCommandOnly(true)
.requiresConfirmation(true)
.supportsEmbedException(true)
.parameters(parameters)

View File

@@ -46,15 +46,29 @@ public class EditAssignableRolePlaceText extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<ParameterValidator> rolePlaceNameValidator = Arrays.asList(MaxStringLengthValidator.max(AssignableRolePlace.ASSIGNABLE_PLACE_NAME_LIMIT));
Parameter rolePostName = Parameter.builder().name("name").validators(rolePlaceNameValidator).type(String.class).templated(true).build();
Parameter newText = Parameter.builder().name("newText").type(String.class).templated(true).build();
Parameter rolePostName = Parameter
.builder().name("name")
.validators(rolePlaceNameValidator)
.type(String.class)
.templated(true)
.build();
Parameter newText = Parameter
.builder()
.name("newText")
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(rolePostName, newText);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("editAssignableRolePlaceText")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.async(true)
.messageCommandOnly(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -67,6 +67,7 @@ public class MoveAssignableRolePlace extends AbstractConditionableCommand {
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.messageCommandOnly(true)
.async(true)
.supportsEmbedException(true)
.parameters(parameters)

View File

@@ -35,15 +35,31 @@ public class RemoveAssignableRoleCondition extends AbstractConditionableCommand
@Override
public CommandConfiguration getConfiguration() {
Parameter placeName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter role = Parameter.builder().name("role").type(Role.class).templated(true).build();
Parameter conditionKey = Parameter.builder().name("conditionKey").type(AssignableRoleConditionType.class).templated(true).build();
Parameter placeName = Parameter
.builder()
.name("name")
.type(String.class)
.templated(true)
.build();
Parameter role = Parameter
.builder()
.name("role")
.type(Role.class)
.templated(true)
.build();
Parameter conditionKey = Parameter
.builder()
.name("conditionKey")
.type(AssignableRoleConditionType.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(placeName, role, conditionKey);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("removeAssignableRoleCondition")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.messageCommandOnly(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -44,16 +44,30 @@ public class RemoveRoleFromAssignableRolePlace extends AbstractConditionableComm
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter role = Parameter.builder().name("role").type(ARole.class).templated(true).build();
Parameter rolePostName = Parameter
.builder()
.name("name")
.type(String.class)
.templated(true)
.build();
Parameter role = Parameter
.builder()
.name("role")
.type(ARole.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(rolePostName, role);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("removeRoleFromAssignableRolePlace")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.async(true)
.messageCommandOnly(true)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -48,14 +48,23 @@ public class SetupAssignableRolePlace extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter rolePostName = Parameter
.builder()
.name("name")
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(rolePostName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("setupAssignableRolePlace")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.async(true)
.messageCommandOnly(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -48,15 +48,24 @@ public class ShowAssignableRolePlaceConfig extends AbstractConditionableCommand
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter rolePostName = Parameter
.builder()
.name("name")
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(rolePostName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("showAssignableRolePlaceConfig")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.async(true)
.causesReaction(false)
.messageCommandOnly(true)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -45,11 +45,15 @@ public class ShowAssignableRolePlaces extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("showAssignableRolePlaces")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.messageCommandOnly(true)
.async(true)
.supportsEmbedException(true)
.help(helpInfo)

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>custom-command</artifactId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -89,6 +89,7 @@ public class CreateCustomCommand extends AbstractConditionableCommand {
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.async(true)
.slashCommandOnly(true)
.slashCommandConfig(slashCommandConfig)
.causesReaction(true)
.supportsEmbedException(true)

View File

@@ -48,11 +48,6 @@ public class DeleteCustomCommand extends AbstractConditionableCommand {
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public FeatureDefinition getFeature() {
return CustomCommandFeatureDefinition.CUSTOM_COMMAND;
}
@Override
public CommandConfiguration getConfiguration() {
Parameter commandNameParameter = Parameter
@@ -82,9 +77,15 @@ public class DeleteCustomCommand extends AbstractConditionableCommand {
.async(true)
.slashCommandConfig(slashCommandConfig)
.causesReaction(true)
.slashCommandOnly(true)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return CustomCommandFeatureDefinition.CUSTOM_COMMAND;
}
}

View File

@@ -9,16 +9,19 @@ 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.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandAutoCompleteService;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.customcommand.config.CustomCommandFeatureDefinition;
import dev.sheldan.abstracto.customcommand.config.CustomCommandSlashCommandNames;
import dev.sheldan.abstracto.customcommand.model.command.CustomCommandResponseModel;
import dev.sheldan.abstracto.customcommand.model.database.CustomCommand;
import dev.sheldan.abstracto.customcommand.service.management.CustomCommandService;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
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;
@@ -39,6 +42,9 @@ public class GetCustomCommand extends AbstractConditionableCommand {
@Autowired
private CustomCommandService customCommandService;
@Autowired
private SlashCommandAutoCompleteService slashCommandAutoCompleteService;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String name = slashCommandParameterService.getCommandOption(CUSTOM_COMMAND_NAME_PARAMETER, event, String.class);
@@ -51,6 +57,20 @@ public class GetCustomCommand extends AbstractConditionableCommand {
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public List<String> performAutoComplete(CommandAutoCompleteInteractionEvent event) {
if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), CUSTOM_COMMAND_NAME_PARAMETER)) {
String input = event.getFocusedOption().getValue();
return customCommandService.getCustomCommandsStartingWith(input, event.getGuild())
.stream()
.map(CustomCommand::getName)
.limit(25)
.toList();
} else {
return new ArrayList<>();
}
}
@Override
public FeatureDefinition getFeature() {
return CustomCommandFeatureDefinition.CUSTOM_COMMAND;
@@ -62,10 +82,10 @@ public class GetCustomCommand extends AbstractConditionableCommand {
.builder()
.name(CUSTOM_COMMAND_NAME_PARAMETER)
.templated(true)
.supportsAutoComplete(true)
.type(String.class)
.build();
List<Parameter> parameters = Arrays.asList(commandNameParameter);
HelpInfo helpInfo = HelpInfo
.builder()
@@ -84,6 +104,7 @@ public class GetCustomCommand extends AbstractConditionableCommand {
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.async(true)
.slashCommandOnly(true)
.slashCommandConfig(slashCommandConfig)
.causesReaction(true)
.supportsEmbedException(true)

View File

@@ -76,6 +76,7 @@ public class ListCustomCommands extends AbstractConditionableCommand {
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.async(true)
.slashCommandOnly(true)
.slashCommandConfig(slashCommandConfig)
.causesReaction(true)
.supportsEmbedException(true)

View File

@@ -13,4 +13,5 @@ public interface CustomCommandRepository extends JpaRepository<CustomCommand, Lo
Optional<CustomCommand> getByNameIgnoreCaseAndServer(String name, AServer server);
void deleteByNameAndServer(String name, AServer server);
List<CustomCommand> findByServer(AServer server);
List<CustomCommand> findByNameStartsWithIgnoreCaseAndServer(String prefix, AServer server);
}

View File

@@ -57,4 +57,10 @@ public class CustomCommandServiceBean implements CustomCommandService {
return customCommandManagementService.getCustomCommandByName(name, guild.getIdLong())
.orElseThrow(CustomCommandNotFoundException::new);
}
@Override
public List<CustomCommand> getCustomCommandsStartingWith(String prefix, Guild guild) {
AServer server = serverManagementService.loadServer(guild);
return customCommandManagementService.getCustomCommandsStartingWith(prefix, server);
}
}

View File

@@ -48,4 +48,9 @@ public class CustomCommandManagementServiceBean implements CustomCommandManageme
return repository.findByServer(server);
}
@Override
public List<CustomCommand> getCustomCommandsStartingWith(String prefix, AServer server) {
return repository.findByNameStartsWithIgnoreCaseAndServer(prefix, server);
}
}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>custom-command</artifactId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -12,4 +12,5 @@ public interface CustomCommandManagementService {
CustomCommand createCustomCommand(String name, String content, AUserInAServer creator);
void deleteCustomCommand(String name, AServer server);
List<CustomCommand> getCustomCommands(AServer server);
List<CustomCommand> getCustomCommandsStartingWith(String prefix, AServer server);
}

View File

@@ -11,4 +11,5 @@ public interface CustomCommandService {
void deleteCustomCommand(String name, Guild guild);
List<CustomCommand> getCustomCommands(Guild guild);
CustomCommand getCustomCommand(String name, Guild guild);
List<CustomCommand> getCustomCommandsStartingWith(String prefix, Guild guild);
}

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>dynamic-activity</artifactId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>dynamic-activity</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>entertainment</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -52,14 +52,28 @@ public class React extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("message").type(Message.class).templated(true).build());
parameters.add(Parameter.builder().name("text").type(String.class).remainder(true).templated(true).build());
Parameter messageParameter = Parameter
.builder()
.name("message")
.type(Message.class)
.templated(true)
.build();
parameters.add(messageParameter);
Parameter textParameter = Parameter
.builder()
.name("text")
.type(String.class)
.remainder(true)
.templated(true)
.build();
parameters.add(textParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("react")
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
.templated(true)
.causesReaction(true)
.messageCommandOnly(true)
.async(true)
.supportsEmbedException(true)
.parameters(parameters)

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>entertainment</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -1,12 +0,0 @@
package dev.sheldan.abstracto.entertainment.model.exception;
import lombok.Builder;
import lombok.Getter;
import java.time.Duration;
@Builder
@Getter
public class PayDayCooldownExceptionModel {
private Duration tryAgainDuration;
}

View File

@@ -1,12 +0,0 @@
package dev.sheldan.abstracto.entertainment.model.exception;
import lombok.Builder;
import lombok.Getter;
import java.time.Duration;
@Builder
@Getter
public class SlotsCooldownExceptionModel {
private Duration tryAgainDuration;
}

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking</artifactId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -69,6 +69,7 @@ public class SetExpRole extends AbstractConditionableCommand {
.module(ExperienceModuleDefinition.EXPERIENCE)
.templated(true)
.async(true)
.messageCommandOnly(true)
.supportsEmbedException(true)
.causesReaction(true)
.requiresConfirmation(true)

View File

@@ -46,7 +46,10 @@ public class SyncRoles extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
return CommandConfiguration.builder()
.name("syncExpRoles")
.module(ExperienceModuleDefinition.EXPERIENCE)
@@ -54,6 +57,7 @@ public class SyncRoles extends AbstractConditionableCommand {
.async(true)
.requiresConfirmation(true)
.supportsEmbedException(true)
.messageCommandOnly(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -59,13 +59,20 @@ public class UnSetExpRole extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("role").templated(true).type(ARole.class).build());
Parameter roleParameter = Parameter
.builder()
.name("role")
.templated(true)
.type(ARole.class)
.build();
parameters.add(roleParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("unSetExpRole")
.module(ExperienceModuleDefinition.EXPERIENCE)
.templated(true)
.async(true)
.messageCommandOnly(true)
.causesReaction(true)
.requiresConfirmation(true)
.supportsEmbedException(true)

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking</artifactId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,103 @@
package dev.sheldan.abstracto.giveaway.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.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import dev.sheldan.abstracto.giveaway.config.GiveawayFeatureDefinition;
import dev.sheldan.abstracto.giveaway.config.GiveawaySlashCommandNames;
import dev.sheldan.abstracto.giveaway.service.GiveawayService;
import jakarta.transaction.Transactional;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class CancelGiveaway extends AbstractConditionableCommand {
private static final String COMMAND_NAME = "cancelGiveaway";
private static final String ID_PARAMETER = "id";
private static final String CANCEL_GIVEAWAY_RESPONSE_TEMPLATE_KEY = "cancelGiveaway_response";
@Autowired
private InteractionService interactionService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private GiveawayService giveawayService;
@Autowired
private CancelGiveaway self;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
return event.deferReply().submit().thenAccept(interactionHook -> {
self.cancelGiveaway(event);
}).thenCompose(unused -> {
List<CompletableFuture<Message>> futures = interactionService.sendMessageToInteraction(CANCEL_GIVEAWAY_RESPONSE_TEMPLATE_KEY, new Object(), event.getHook());
return new CompletableFutureList<>(futures).getMainFuture();
})
.thenApply(unused -> CommandResult.fromSuccess());
}
@Transactional
public void cancelGiveaway(SlashCommandInteractionEvent event) {
Long giveawayId = slashCommandParameterService.getCommandOption(ID_PARAMETER, event, Integer.class).longValue();
giveawayService.cancelGiveaway(giveawayId, event.getGuild().getIdLong());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter giveawayIdParameter = Parameter
.builder()
.templated(true)
.name(ID_PARAMETER)
.type(Integer.class)
.build();
List<Parameter> parameters = Arrays.asList(giveawayIdParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(GiveawaySlashCommandNames.GIVEAWAY)
.commandName("cancel")
.build();
return CommandConfiguration.builder()
.name(COMMAND_NAME)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return GiveawayFeatureDefinition.GIVEAWAY;
}
}

View File

@@ -0,0 +1,182 @@
package dev.sheldan.abstracto.giveaway.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.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.utils.ParseUtils;
import dev.sheldan.abstracto.giveaway.config.GiveawayFeatureDefinition;
import dev.sheldan.abstracto.giveaway.config.GiveawaySlashCommandNames;
import dev.sheldan.abstracto.giveaway.model.GiveawayCreationRequest;
import dev.sheldan.abstracto.giveaway.service.GiveawayService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
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.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class GreateGiveaway extends AbstractConditionableCommand {
private static final String COMMAND_NAME = "createGiveaway";
private static final String TITLE_PARAMETER = "title";
private static final String DESCRIPTION_PARAMETER = "description";
private static final String BENEFACTOR_PARAMETER = "benefactor";
private static final String CHANNEL_PARAMETER = "channel";
private static final String DURATION_PARAMETER = "duration";
private static final String WINNERS_PARAMETER = "winners";
private static final String CREATE_GIVEAWAY_RESPONSE_TEMPLATE_KEY = "createGiveaway_response";
@Autowired
private InteractionService interactionService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private GiveawayService giveawayService;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
return event.deferReply()
.submit()
.thenCompose(interactionHook -> {
String title = slashCommandParameterService.getCommandOption(TITLE_PARAMETER, event, String.class);
String description;
if(slashCommandParameterService.hasCommandOption(DESCRIPTION_PARAMETER, event)) {
description = slashCommandParameterService.getCommandOption(DESCRIPTION_PARAMETER, event, String.class);
} else {
description = null;
}
String durationString = slashCommandParameterService.getCommandOption(DURATION_PARAMETER, event, Duration.class, String.class);
Duration duration = ParseUtils.parseDuration(durationString);
GuildMessageChannel target = null;
if(slashCommandParameterService.hasCommandOption(CHANNEL_PARAMETER, event)) {
target = slashCommandParameterService.getCommandOption(CHANNEL_PARAMETER, event, GuildMessageChannel.class);
}
Integer winners = 1;
if(slashCommandParameterService.hasCommandOption(WINNERS_PARAMETER, event)) {
winners = slashCommandParameterService.getCommandOption(WINNERS_PARAMETER, event, Integer.class);
}
Member benefactor;
if(slashCommandParameterService.hasCommandOption(BENEFACTOR_PARAMETER, event)) {
benefactor = slashCommandParameterService.getCommandOption(BENEFACTOR_PARAMETER, event, Member.class);
} else {
benefactor = null;
}
Member creator = event.getMember();
GiveawayCreationRequest request = GiveawayCreationRequest
.builder()
.benefactor(benefactor)
.creator(creator)
.description(description)
.duration(duration)
.targetChannel(target)
.winnerCount(winners)
.title(title)
.build();
return giveawayService.createGiveaway(request)
.thenAccept(unused -> interactionService.sendEmbed(CREATE_GIVEAWAY_RESPONSE_TEMPLATE_KEY, interactionHook));
}).thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter titleParameter = Parameter
.builder()
.templated(true)
.name(TITLE_PARAMETER)
.type(String.class)
.build();
Parameter descriptionParameter = Parameter
.builder()
.templated(true)
.name(DESCRIPTION_PARAMETER)
.type(String.class)
.optional(true)
.build();
Parameter channelParameter = Parameter
.builder()
.name(CHANNEL_PARAMETER)
.type(GuildMessageChannel.class)
.optional(true)
.templated(true)
.build();
Parameter durationParameter = Parameter
.builder()
.name(DURATION_PARAMETER)
.type(Duration.class)
.templated(true)
.build();
Parameter winnersParameter = Parameter
.builder()
.name(WINNERS_PARAMETER)
.type(Integer.class)
.optional(true)
.templated(true)
.build();
Parameter benefactorParameter = Parameter
.builder()
.templated(true)
.name(BENEFACTOR_PARAMETER)
.type(Member.class)
.optional(true)
.build();
List<Parameter> parameters = Arrays.asList(titleParameter, durationParameter, benefactorParameter, descriptionParameter,
channelParameter, winnersParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(GiveawaySlashCommandNames.GIVEAWAY)
.commandName("create")
.build();
return CommandConfiguration.builder()
.name(COMMAND_NAME)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return GiveawayFeatureDefinition.GIVEAWAY;
}
}

View File

@@ -0,0 +1,103 @@
package dev.sheldan.abstracto.giveaway.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.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import dev.sheldan.abstracto.giveaway.config.GiveawayFeatureDefinition;
import dev.sheldan.abstracto.giveaway.config.GiveawaySlashCommandNames;
import dev.sheldan.abstracto.giveaway.service.GiveawayService;
import jakarta.transaction.Transactional;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class ReRollGiveaway extends AbstractConditionableCommand {
private static final String COMMAND_NAME = "reRollGiveaway";
private static final String ID_PARAMETER = "id";
private static final String RE_ROLL_GIVEAWAY_RESPONSE_TEMPLATE_KEY = "reRollGiveaway_response";
@Autowired
private InteractionService interactionService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private GiveawayService giveawayService;
@Autowired
private ReRollGiveaway self;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
return event.deferReply().submit().thenAccept(interactionHook -> {
self.reRollGiveaway(event);
}).thenCompose(unused -> {
List<CompletableFuture<Message>> futures = interactionService.sendMessageToInteraction(RE_ROLL_GIVEAWAY_RESPONSE_TEMPLATE_KEY, new Object(), event.getHook());
return new CompletableFutureList<>(futures).getMainFuture();
})
.thenApply(unused -> CommandResult.fromSuccess());
}
@Transactional
public void reRollGiveaway(SlashCommandInteractionEvent event) {
Long giveawayId = slashCommandParameterService.getCommandOption(ID_PARAMETER, event, Integer.class).longValue();
giveawayService.evaluateGiveaway(giveawayId, event.getGuild().getIdLong());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter giveawayIdParameter = Parameter
.builder()
.templated(true)
.name(ID_PARAMETER)
.type(Integer.class)
.build();
List<Parameter> parameters = Arrays.asList(giveawayIdParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(GiveawaySlashCommandNames.GIVEAWAY)
.commandName("reroll")
.build();
return CommandConfiguration.builder()
.name(COMMAND_NAME)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return GiveawayFeatureDefinition.GIVEAWAY;
}
}

View File

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

View File

@@ -0,0 +1,41 @@
package dev.sheldan.abstracto.giveaway.job;
import dev.sheldan.abstracto.giveaway.service.GiveawayService;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.PersistJobDataAfterExecution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
@Slf4j
@DisallowConcurrentExecution
@Component
@PersistJobDataAfterExecution
public class GiveawayEvaluationJob extends QuartzJobBean {
@Getter
@Setter
private Long giveawayId;
@Getter
@Setter
private Long serverId;
@Autowired
private GiveawayService giveawayService;
@Override
protected void executeInternal(JobExecutionContext context) {
try {
log.info("Executing giveaway evaluation job for giveaway {} in server {}", giveawayId, serverId);
giveawayService.evaluateGiveaway(giveawayId, serverId);
} catch (Exception exception) {
log.error("Giveaway evaluation job failed.", exception);
}
}
}

View File

@@ -0,0 +1,94 @@
package dev.sheldan.abstracto.giveaway.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListener;
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerModel;
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerResult;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.giveaway.config.GiveawayFeatureDefinition;
import dev.sheldan.abstracto.giveaway.exception.GiveawayNotFoundException;
import dev.sheldan.abstracto.giveaway.model.JoinGiveawayPayload;
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
import dev.sheldan.abstracto.giveaway.service.GiveawayService;
import dev.sheldan.abstracto.giveaway.service.GiveawayServiceBean;
import dev.sheldan.abstracto.giveaway.service.management.GiveawayManagementService;
import dev.sheldan.abstracto.giveaway.service.management.GiveawayParticipantManagementService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
@Slf4j
public class GiveawayJoinListener implements ButtonClickedListener {
private static final String GIVEAWAY_JOIN_RESPONSE_TEMPLATE_KEY = "giveaway_join_response";
private static final String GIVEAWAY_ALREADY_JOINED_RESPONSE_TEMPLATE_KEY = "giveaway_already_joined_response";
@Autowired
private GiveawayManagementService giveawayManagementService;
@Autowired
private GiveawayService giveawayService;
@Autowired
private InteractionService interactionService;
@Autowired
private GiveawayParticipantManagementService giveawayParticipantManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
JoinGiveawayPayload payload = (JoinGiveawayPayload) model.getDeserializedPayload();
Optional<Giveaway> optionalGiveaway = giveawayManagementService.loadGiveawayById(payload.getGiveawayId(), payload.getServerId());
if(optionalGiveaway.isPresent()) {
Giveaway giveaway = optionalGiveaway.get();
AUserInAServer user = userInServerManagementService.loadOrCreateUser(model.getEvent().getMember());
Long joiningUserId = model.getEvent().getMember().getIdLong();
if(!giveawayParticipantManagementService.userIsAlreadyParticipating(giveaway, user)) {
log.info("Adding user {} to giveaway {}.", joiningUserId, payload.getGiveawayId());
giveawayService.addGiveawayParticipant(giveaway, model.getEvent().getMember(), model.getEvent().getMessageChannel())
.thenAccept(unused -> {
log.info("Notified user {} in giveaway {} join event.", joiningUserId, payload.getGiveawayId());
interactionService.replyEmbed(GIVEAWAY_JOIN_RESPONSE_TEMPLATE_KEY, model.getEvent());
}).exceptionally(throwable -> {
log.error("Failed to add {} to giveaway {}.", joiningUserId, payload.getGiveawayId(), throwable);
return null;
});
} else {
log.info("User {} was already part of giveaway {}.", joiningUserId, payload.getGiveawayId());
interactionService.replyEmbed(GIVEAWAY_ALREADY_JOINED_RESPONSE_TEMPLATE_KEY, model.getEvent());
}
return ButtonClickedListenerResult.ACKNOWLEDGED;
} else {
throw new GiveawayNotFoundException();
}
}
@Override
public Boolean handlesEvent(ButtonClickedListenerModel model) {
return GiveawayServiceBean.GIVEAWAY_JOIN_ORIGIN.equals(model.getOrigin());
}
@Override
public FeatureDefinition getFeature() {
return GiveawayFeatureDefinition.GIVEAWAY;
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
@Override
public Boolean autoAcknowledgeEvent() {
return false;
}
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.giveaway.repository;
import dev.sheldan.abstracto.giveaway.model.database.GiveawayParticipant;
import dev.sheldan.abstracto.giveaway.model.database.embed.GiveawayParticipationId;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface GiveawayParticipantRepository extends JpaRepository<GiveawayParticipant, GiveawayParticipationId> {
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.giveaway.repository;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface GiveawayRepository extends JpaRepository<Giveaway, ServerSpecificId> {
}

View File

@@ -0,0 +1,275 @@
package dev.sheldan.abstracto.giveaway.service;
import dev.sheldan.abstracto.core.interaction.ComponentPayloadService;
import dev.sheldan.abstracto.core.interaction.ComponentService;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.CounterService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
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.CompletableFutureList;
import dev.sheldan.abstracto.giveaway.config.GiveawayPostTarget;
import dev.sheldan.abstracto.giveaway.exception.GiveawayNotFoundException;
import dev.sheldan.abstracto.giveaway.model.GiveawayCreationRequest;
import dev.sheldan.abstracto.giveaway.model.JoinGiveawayPayload;
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
import dev.sheldan.abstracto.giveaway.model.database.GiveawayParticipant;
import dev.sheldan.abstracto.giveaway.model.template.GiveawayMessageModel;
import dev.sheldan.abstracto.giveaway.model.template.GiveawayResultMessageModel;
import dev.sheldan.abstracto.giveaway.service.management.GiveawayManagementService;
import dev.sheldan.abstracto.giveaway.service.management.GiveawayParticipantManagementService;
import dev.sheldan.abstracto.scheduling.model.JobParameters;
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import jakarta.transaction.Transactional;
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.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class GiveawayServiceBean implements GiveawayService {
private static final String GIVEAWAY_MESSAGE_TEMPLATE_KEY = "giveaway_post";
private static final String GIVEAWAY_RESULT_MESSAGE_TEMPLATE_KEY = "giveaway_result";
public static final String GIVEAWAY_JOIN_ORIGIN = "JOIN_GIVEAWAY";
public static final String GIVEAWAY_COUNTER = "giveaways";
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private GiveawayManagementService giveawayManagementService;
@Autowired
private ComponentService componentService;
@Autowired
private PostTargetService postTargetService;
@Autowired
private ChannelService channelService;
@Autowired
private TemplateService templateService;
@Autowired
private ComponentPayloadService componentPayloadService;
@Autowired
private GiveawayParticipantManagementService giveawayParticipantManagementService;
@Autowired
private SchedulerService schedulerService;
@Autowired
private SecureRandom secureRandom;
@Autowired
private CounterService counterService;
@Autowired
private GiveawayServiceBean self;
@Override
public CompletableFuture<Void> createGiveaway(GiveawayCreationRequest giveawayCreationRequest) {
String componentId = componentService.generateComponentId();
Instant targetDate = Instant.now().plus(giveawayCreationRequest.getDuration());
Long serverId = giveawayCreationRequest.getCreator().getGuild().getIdLong();
Long giveawayId = counterService.getNextCounterValue(serverId, GIVEAWAY_COUNTER);
GiveawayMessageModel model = GiveawayMessageModel
.builder()
.title(giveawayCreationRequest.getTitle())
.description(giveawayCreationRequest.getDescription())
.giveawayId(giveawayId)
.benefactor(giveawayCreationRequest.getBenefactor() != null ? MemberDisplay.fromMember(giveawayCreationRequest.getBenefactor()) : null)
.creator(MemberDisplay.fromMember(giveawayCreationRequest.getCreator()))
.winnerCount(giveawayCreationRequest.getWinnerCount())
.targetDate(targetDate)
.joinComponentId(componentId)
.build();
List<CompletableFuture<Message>> messageFutures;
log.info("Rendering giveaway message in server {} by user {}", serverId, giveawayCreationRequest.getCreator().getIdLong());
MessageToSend messageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, model, serverId);
if(giveawayCreationRequest.getTargetChannel() == null) {
log.info("Sending giveaway to post target in server {}", serverId);
postTargetService.validatePostTarget(GiveawayPostTarget.GIVEAWAYS, giveawayCreationRequest.getCreator().getGuild().getIdLong());
messageFutures = postTargetService.sendEmbedInPostTarget(messageToSend, GiveawayPostTarget.GIVEAWAYS, serverId);
} else {
log.info("Sending giveaway to channel {} in server {}.", giveawayCreationRequest.getTargetChannel().getId(), serverId);
messageFutures = channelService.sendMessageToSendToChannel(messageToSend, giveawayCreationRequest.getTargetChannel());
}
CompletableFutureList<Message> messageFutureList = new CompletableFutureList<>(messageFutures);
return messageFutureList.getMainFuture().thenAccept(o -> {
Message createdMessage = messageFutureList.getFutures().get(0).join();
giveawayCreationRequest.setTargetChannel(createdMessage.getGuildChannel());
self.persistGiveaway(giveawayCreationRequest, giveawayId, createdMessage.getIdLong(), componentId);
});
}
@Override
public CompletableFuture<Void> addGiveawayParticipant(Giveaway giveaway, Member member, MessageChannel messageChannel) {
GiveawayMessageModel giveawayMessageModel = GiveawayMessageModel.fromGiveaway(giveaway);
giveawayMessageModel.setJoinedUserCount(giveaway.getParticipants().size() + 1L);
Long giveawayId = giveaway.getGiveawayId().getId();
log.info("Adding giveaway participating of user {} to giveaway {} in server {}.", member.getIdLong(), giveawayId, member.getGuild().getIdLong());
MessageToSend messageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel);
return channelService.editEmbedMessageInAChannel(messageToSend.getEmbeds().get(0), messageChannel, giveaway.getMessageId())
.thenAccept(message -> {
self.persistAddedParticipant(member, giveawayId);
});
}
@Override
@Transactional
public CompletableFuture<Void> evaluateGiveaway(Long giveawayId, Long serverId) {
Optional<Giveaway> giveAwayOptional = giveawayManagementService.loadGiveawayById(giveawayId, serverId);
if(giveAwayOptional.isEmpty()) {
throw new GiveawayNotFoundException();
}
log.info("Evaluating giveaway {} in server {}.", giveawayId, serverId);
Giveaway giveaway = giveAwayOptional.get();
Set<Long> winners = new HashSet<>();
Integer winnerCount = giveaway.getWinnerCount();
List<Long> potentialWinners = new ArrayList<>(giveaway
.getParticipants()
.stream()
.map(giveawayParticipant -> giveawayParticipant.getParticipant().getUserInServerId())
.toList());
if(potentialWinners.size() <= winnerCount) {
winners.addAll(potentialWinners);
log.debug("Less participants than total winners - selecting all for giveaway {} in server {}.", giveawayId, serverId);
} else {
for (int i = 0; i < winnerCount; i++) {
int winnerIndex = secureRandom.nextInt(potentialWinners.size());
Long winner = potentialWinners.get(winnerIndex);
potentialWinners.remove(winnerIndex);
winners.add(winner);
}
}
List<GiveawayParticipant> winningParticipants = giveaway
.getParticipants()
.stream()
.filter(giveawayParticipant -> winners.contains(giveawayParticipant.getParticipant().getUserInServerId()))
.toList();
List<MemberDisplay> winnerDisplays = winningParticipants
.stream()
.map(giveawayParticipant -> MemberDisplay.fromAUserInAServer(giveawayParticipant.getParticipant()))
.toList();
GiveawayResultMessageModel resultModel = GiveawayResultMessageModel
.builder()
.messageId(giveaway.getMessageId())
.title(giveaway.getTitle())
.winners(winnerDisplays)
.build();
log.info("Sending result message for giveaway {} in server {}.", giveawayId, serverId);
MessageToSend messageToSend = templateService.renderEmbedTemplate(GIVEAWAY_RESULT_MESSAGE_TEMPLATE_KEY, resultModel);
List<CompletableFuture<Message>> resultFutures = channelService.sendMessageEmbedToSendToAChannel(messageToSend, giveaway.getGiveawayChannel());
GiveawayMessageModel giveawayMessageModel = GiveawayMessageModel.fromGiveaway(giveaway);
giveawayMessageModel.setWinners(winnerDisplays);
giveawayMessageModel.setEnded(true);
MessageToSend giveawayMessageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel);
log.info("Updating original giveaway message for giveaway {} in server {}.", giveawayId, serverId);
GuildMessageChannel messageChannel = channelService.getMessageChannelFromServer(giveaway.getServer().getId(), giveaway.getGiveawayChannel().getId());
CompletableFuture<Message> giveawayUpdateFuture = channelService.editMessageInAChannelFuture(giveawayMessageToSend, messageChannel, giveaway.getMessageId());
resultFutures.add(giveawayUpdateFuture);
return new CompletableFutureList<>(resultFutures).getMainFuture();
}
@Override
public CompletableFuture<Void> cancelGiveaway(Long giveawayId, Long serverId) {
Optional<Giveaway> giveAwayOptional = giveawayManagementService.loadGiveawayById(giveawayId, serverId);
if(giveAwayOptional.isEmpty()) {
throw new GiveawayNotFoundException();
}
Giveaway giveaway = giveAwayOptional.get();
log.info("Cancelling giveaway with id {} in server {}.", giveawayId, serverId);
GiveawayMessageModel giveawayMessageModel = GiveawayMessageModel.fromGiveaway(giveaway);
giveawayMessageModel.setCancelled(true);
schedulerService.stopTrigger(giveaway.getReminderTriggerKey());
MessageToSend giveawayMessageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel);
GuildMessageChannel messageChannel = channelService.getMessageChannelFromServer(giveaway.getServer().getId(), giveaway.getGiveawayChannel().getId());
log.debug("Updating original giveaway message to consider cancellation for giveaway {} in server {}.", giveawayId, serverId);
return channelService.editMessageInAChannelFuture(giveawayMessageToSend, messageChannel, giveaway.getMessageId())
.thenAccept(message -> {
self.persistGiveawayCancellation(giveawayId, serverId);
});
}
@Transactional
public void persistGiveawayCancellation(Long giveawayId, Long serverId) {
Optional<Giveaway> giveAwayOptional = giveawayManagementService.loadGiveawayById(giveawayId, serverId);
if(giveAwayOptional.isEmpty()) {
throw new GiveawayNotFoundException();
}
log.info("Persisting cancellation of giveaway {} in server {}.", giveawayId, serverId);
Giveaway giveaway = giveAwayOptional.get();
giveaway.setCancelled(true);
}
@Transactional
public void persistAddedParticipant(Member member, Long giveawayId) {
log.info("Storing user {} as participant to giveaway {} in server {}.", member.getIdLong(), giveawayId, member.getGuild().getIdLong());
Optional<Giveaway> giveAwayOptional = giveawayManagementService.loadGiveawayById(giveawayId, member.getGuild().getIdLong());
giveAwayOptional.ifPresent(giveaway -> {
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
giveawayParticipantManagementService.addParticipant(giveaway, aUserInAServer);
});
}
@Transactional
public void persistGiveaway(GiveawayCreationRequest giveawayCreationRequest, Long giveawayId, Long messageId, String componentId) {
Member creatorMember = giveawayCreationRequest.getCreator();
log.info("Persisting giveaway in server {} with message id {}.", creatorMember.getGuild().getIdLong(), messageId);
Instant targetDate = Instant.now().plus(giveawayCreationRequest.getDuration());
AChannel targetChannel = channelManagementService.loadChannel(giveawayCreationRequest.getTargetChannel().getIdLong());
AUserInAServer creator = userInServerManagementService.loadOrCreateUser(creatorMember);
AUserInAServer benefactor;
if(giveawayCreationRequest.getBenefactor() != null) {
benefactor = userInServerManagementService.loadOrCreateUser(giveawayCreationRequest.getBenefactor());
} else {
benefactor = null;
}
Giveaway giveaway = giveawayManagementService.createGiveaway(creator, benefactor, targetChannel, targetDate,
giveawayCreationRequest.getTitle(), giveawayCreationRequest.getDescription(), giveawayCreationRequest.getWinnerCount(),
messageId, componentId, giveawayId);
HashMap<Object, Object> parameters = new HashMap<>();
parameters.put("giveawayId", giveaway.getGiveawayId().getId().toString());
parameters.put("serverId", giveaway.getGiveawayId().getServerId().toString());
JobParameters jobParameters = JobParameters
.builder()
.parameters(parameters)
.build();
log.info("Scheduling giveaway reminder for giveaway {} originating from message {} in server {}.", giveaway.getGiveawayId().getId(), messageId, creatorMember.getGuild().getIdLong());
String triggerKey = schedulerService.executeJobWithParametersOnce("giveawayEvaluationJob", "giveaway", jobParameters, Date.from(giveaway.getTargetDate()));
giveaway.setReminderTriggerKey(triggerKey);
JoinGiveawayPayload joinPayload = JoinGiveawayPayload
.builder()
.giveawayId(giveaway.getGiveawayId().getId())
.serverId(giveaway.getGiveawayId().getServerId())
.build();
componentPayloadService.createButtonPayload(componentId, joinPayload, GIVEAWAY_JOIN_ORIGIN, creator.getServerReference());
}
}

View File

@@ -0,0 +1,46 @@
package dev.sheldan.abstracto.giveaway.service.management;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
import dev.sheldan.abstracto.giveaway.repository.GiveawayRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.Optional;
@Component
public class GiveawayManagementServiceBean implements GiveawayManagementService {
@Autowired
private GiveawayRepository giveawayRepository;
@Override
public Giveaway createGiveaway(AUserInAServer creator, AUserInAServer benefactor, AChannel target,
Instant targetDate, String title, String description, Integer winnerCount,
Long messageId, String componentId, Long giveawayId) {
Giveaway giveaway = Giveaway
.builder()
.giveawayId(new ServerSpecificId(creator.getServerReference().getId(), giveawayId))
.creator(creator)
.benefactor(benefactor)
.messageId(messageId)
.componentId(componentId)
.server(creator.getServerReference())
.winnerCount(winnerCount)
.cancelled(false)
.title(title)
.giveawayChannel(target)
.description(description)
.targetDate(targetDate)
.build();
return giveawayRepository.save(giveaway);
}
@Override
public Optional<Giveaway> loadGiveawayById(Long giveawayId, Long serverId) {
return giveawayRepository.findById(new ServerSpecificId(serverId, giveawayId));
}
}

View File

@@ -0,0 +1,34 @@
package dev.sheldan.abstracto.giveaway.service.management;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
import dev.sheldan.abstracto.giveaway.model.database.GiveawayParticipant;
import dev.sheldan.abstracto.giveaway.model.database.embed.GiveawayParticipationId;
import dev.sheldan.abstracto.giveaway.repository.GiveawayParticipantRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class GiveawayParticipantManagementServiceBean implements GiveawayParticipantManagementService {
@Autowired
private GiveawayParticipantRepository repository;
@Override
public void addParticipant(Giveaway giveaway, AUserInAServer aUserInAServer) {
GiveawayParticipationId id = new GiveawayParticipationId(aUserInAServer.getUserInServerId(), giveaway.getGiveawayId().getId(), giveaway.getServer().getId());
GiveawayParticipant participant = GiveawayParticipant
.builder()
.id(id)
.giveaway(giveaway)
.participant(aUserInAServer)
.won(false)
.build();
repository.save(participant);
}
@Override
public boolean userIsAlreadyParticipating(Giveaway giveaway, AUserInAServer aUserInAServer) {
return repository.existsById(new GiveawayParticipationId(aUserInAServer.getUserInServerId(), giveaway.getGiveawayId().getId(), giveaway.getServer().getId()));
}
}

View File

@@ -0,0 +1,4 @@
abstracto.featureFlags.giveaway.featureName=giveaway
abstracto.featureFlags.giveaway.enabled=false
abstracto.postTargets.giveaways.name=giveaways

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="giveaway_evaluation_job-insert">
<insert tableName="scheduler_job">
<column name="name" value="giveawayEvaluationJob"/>
<column name="group_name" value="giveaway"/>
<column name="clazz" value="dev.sheldan.abstracto.giveaway.job.GiveawayEvaluationJob"/>
<column name="active" value="true"/>
<column name="recovery" value="false"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,75 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="giveaway-table">
<createTable tableName="giveaway">
<column name="id" type="BIGINT">
<constraints nullable="false" />
</column>
<column name="creator_user_id" type="INTEGER">
<constraints nullable="false"/>
</column>
<column name="benefactor_user_id" type="INTEGER">
<constraints nullable="true"/>
</column>
<column name="title" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="component_id" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
<column name="description" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
<column name="reminder_trigger_key" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
<column name="giveaway_channel_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="message_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="cancelled" type="BOOLEAN">
<constraints nullable="false"/>
</column>
<column name="target_date" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="winner_count" type="INTEGER">
<constraints nullable="false"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addPrimaryKey tableName="giveaway" columnNames="id, server_id"/>
<addForeignKeyConstraint baseColumnNames="creator_user_id" baseTableName="giveaway" constraintName="fk_giveaway_creator" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id"
referencedTableName="user_in_server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="benefactor_user_id" baseTableName="giveaway" constraintName="fk_giveaway_benefactor" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id"
referencedTableName="user_in_server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="giveaway_channel_id" baseTableName="giveaway" constraintName="fk_giveaway_channel" deferrable="false"
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="channel" validate="true"/>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="giveaway" constraintName="fk_giveaway_server" deferrable="false" initiallyDeferred="false"
onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS giveaway_update_trigger ON giveaway;
CREATE TRIGGER giveaway_update_trigger BEFORE UPDATE ON giveaway FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS giveaway_insert_trigger ON giveaway;
CREATE TRIGGER giveaway_insert_trigger BEFORE INSERT ON giveaway FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,45 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="giveaway_participant-table">
<createTable tableName="giveaway_participant">
<column name="participant_user_in_server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="giveaway_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="won" type="BOOLEAN">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addPrimaryKey columnNames="participant_user_in_server_id, giveaway_id, server_id" tableName="giveaway_participant" constraintName="pk_giveaway_participant" validate="true"/>
<addForeignKeyConstraint baseColumnNames="participant_user_in_server_id" baseTableName="giveaway_participant" constraintName="fk_giveaway_participant_participant"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id"
referencedTableName="user_in_server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="giveaway_id, server_id" baseTableName="giveaway_participant" constraintName="fk_giveaway_participant_giveaway"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id, server_id"
referencedTableName="giveaway" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS giveaway_participant_update_trigger ON giveaway_participant;
CREATE TRIGGER giveaway_participant_update_trigger BEFORE UPDATE ON giveaway_participant FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS giveaway_participant_insert_trigger ON giveaway_participant;
CREATE TRIGGER giveaway_participant_insert_trigger BEFORE INSERT ON giveaway_participant FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>giveaway</artifactId>
<version>1.5.13</version>
</parent>
<artifactId>giveaway-int</artifactId>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,23 @@
package dev.sheldan.abstracto.giveaway.config;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.PostTargetEnum;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class GiveawayFeatureConfig implements FeatureConfig {
@Override
public FeatureDefinition getFeature() {
return GiveawayFeatureDefinition.GIVEAWAY;
}
@Override
public List<PostTargetEnum> getRequiredPostTargets() {
return Arrays.asList(GiveawayPostTarget.GIVEAWAYS);
}
}

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
package dev.sheldan.abstracto.giveaway.config;
public class GiveawaySlashCommandNames {
public static final String GIVEAWAY = "giveaway";
}

View File

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

View File

@@ -0,0 +1,23 @@
package dev.sheldan.abstracto.giveaway.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import java.time.Duration;
@Builder
@Getter
public class GiveawayCreationRequest {
private Member creator;
private Member benefactor;
private String title;
private String description;
private Duration duration;
@Setter
private GuildMessageChannel targetChannel;
private Integer winnerCount;
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.giveaway.model;
import dev.sheldan.abstracto.core.interaction.button.ButtonPayload;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class JoinGiveawayPayload implements ButtonPayload {
private Long giveawayId;
private Long serverId;
}

View File

@@ -0,0 +1,82 @@
package dev.sheldan.abstracto.giveaway.model.database;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
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 jakarta.persistence.*;
import lombok.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "giveaway")
@Getter
@Setter
@EqualsAndHashCode
public class Giveaway {
@Id
@EmbeddedId
private ServerSpecificId giveawayId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "creator_user_id", nullable = false)
private AUserInAServer creator;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "benefactor_user_id")
private AUserInAServer benefactor;
@Column(name = "title", nullable = false)
private String title;
@Column(name = "description")
private String description;
@Column(name = "component_id")
private String componentId;
@Column(name = "winner_count", nullable = false)
private Integer winnerCount;
@Column(name = "target_date", nullable = false)
private Instant targetDate;
@Column(name = "cancelled", nullable = false)
private Boolean cancelled;
@Column(name = "reminder_trigger_key")
private String reminderTriggerKey;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "giveaway_channel_id", nullable = false)
private AChannel giveawayChannel;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@MapsId("serverId")
@JoinColumn(name = "server_id", referencedColumnName = "id", nullable = false)
private AServer server;
@Column(name = "message_id", nullable = false)
private Long messageId;
@Column(name = "created", nullable = false, insertable = false, updatable = false)
private Instant created;
@Column(name = "updated", insertable = false, updatable = false)
private Instant updated;
@OneToMany(
fetch = FetchType.LAZY,
orphanRemoval = true,
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
mappedBy = "giveaway")
@Builder.Default
private List<GiveawayParticipant> participants = new ArrayList<>();
}

View File

@@ -0,0 +1,45 @@
package dev.sheldan.abstracto.giveaway.model.database;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.giveaway.model.database.embed.GiveawayParticipationId;
import jakarta.persistence.*;
import lombok.*;
import java.time.Instant;
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "giveaway_participant")
@Getter
@Setter
@EqualsAndHashCode
public class GiveawayParticipant {
@EmbeddedId
@Getter
private GiveawayParticipationId id;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
@MapsId("participantId")
@JoinColumn(name = "participant_user_in_server_id", nullable = false)
private AUserInAServer participant;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns(
{
@JoinColumn(updatable = false, insertable = false, name = "giveaway_id", referencedColumnName = "id"),
@JoinColumn(updatable = false, insertable = false, name = "server_id", referencedColumnName = "server_id")
})
private Giveaway giveaway;
@Column(name = "won", nullable = false)
private Boolean won;
@Column(name = "created", nullable = false, insertable = false, updatable = false)
private Instant created;
@Column(name = "updated", insertable = false, updatable = false)
private Instant updated;
}

View File

@@ -0,0 +1,26 @@
package dev.sheldan.abstracto.giveaway.model.database.embed;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.*;
import java.io.Serializable;
@Embeddable
@Getter
@Setter
@Builder
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class GiveawayParticipationId implements Serializable {
@Column(name = "participant_user_in_server_id")
private Long participantId;
@Column(name = "giveaway_id")
private Long giveawayId;
@Column(name = "server_id")
private Long serverId;
}

View File

@@ -0,0 +1,48 @@
package dev.sheldan.abstracto.giveaway.model.template;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Builder
@Getter
@Setter
public class GiveawayMessageModel {
private String title;
private String description;
private Integer winnerCount;
private Long giveawayId;
@Builder.Default
private Boolean ended = false;
@Builder.Default
private Boolean cancelled = false;
@Builder.Default
private Long joinedUserCount = 0L;
private MemberDisplay creator;
private MemberDisplay benefactor;
private Instant targetDate;
private String joinComponentId;
@Builder.Default
private List<MemberDisplay> winners = new ArrayList<>();
public static GiveawayMessageModel fromGiveaway(Giveaway giveaway) {
return GiveawayMessageModel
.builder()
.title(giveaway.getTitle())
.description(giveaway.getDescription())
.benefactor(giveaway.getBenefactor() != null ? MemberDisplay.fromAUserInAServer(giveaway.getBenefactor()) : null)
.creator(MemberDisplay.fromAUserInAServer(giveaway.getCreator()))
.winnerCount(giveaway.getWinnerCount())
.joinedUserCount((long) giveaway.getParticipants().size())
.joinComponentId(giveaway.getComponentId())
.giveawayId(giveaway.getGiveawayId().getId())
.targetDate(giveaway.getTargetDate())
.build();
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.giveaway.model.template;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import lombok.Builder;
import lombok.Getter;
import java.util.List;
@Getter
@Builder
public class GiveawayResultMessageModel {
private String title;
private Long messageId;
private List<MemberDisplay> winners;
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.giveaway.service;
import dev.sheldan.abstracto.giveaway.model.GiveawayCreationRequest;
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import java.util.concurrent.CompletableFuture;
public interface GiveawayService {
CompletableFuture<Void> createGiveaway(GiveawayCreationRequest giveawayCreationRequest);
CompletableFuture<Void> addGiveawayParticipant(Giveaway giveaway, Member member, MessageChannel messageChannel);
CompletableFuture<Void> evaluateGiveaway(Long giveawayId, Long serverId);
CompletableFuture<Void> cancelGiveaway(Long giveawayId, Long serverId);
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.giveaway.service.management;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
import java.time.Instant;
import java.util.Optional;
public interface GiveawayManagementService {
Giveaway createGiveaway(AUserInAServer creator, AUserInAServer benefactor, AChannel target,
Instant targetDate, String title, String description, Integer winnerCount, Long messageId,
String componentId, Long giveawayId);
Optional<Giveaway> loadGiveawayById(Long giveawayId, Long serverId);
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.giveaway.service.management;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
public interface GiveawayParticipantManagementService {
void addParticipant(Giveaway giveaway, AUserInAServer aUserInAServer);
boolean userIsAlreadyParticipating(Giveaway giveaway, AUserInAServer aUserInAServer);
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.5.13</version>
</parent>
<artifactId>giveaway</artifactId>
<packaging>pom</packaging>
<modules>
<module>giveaway-int</module>
<module>giveaway-impl</module>
</modules>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>invite-filter</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -33,13 +33,20 @@ public class AllowInvite extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("invite").type(String.class).templated(true).build());
Parameter inviteParameter = Parameter
.builder()
.name("invite")
.type(String.class)
.templated(true)
.build();
parameters.add(inviteParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("allowInvite")
.module(InviteFilterModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.messageCommandOnly(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -33,12 +33,19 @@ public class DisAllowInvite extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("invite").type(String.class).templated(true).build());
Parameter inviteParameter = Parameter
.builder()
.name("invite")
.type(String.class)
.templated(true)
.build();
parameters.add(inviteParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("disAllowInvite")
.module(InviteFilterModerationModuleDefinition.MODERATION)
.templated(true)
.messageCommandOnly(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)

View File

@@ -44,13 +44,21 @@ public class RemoveTrackedInviteLinks extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("invite").type(String.class).optional(true).templated(true).build());
Parameter inviteParameter = Parameter
.builder()
.name("invite")
.type(String.class)
.optional(true)
.templated(true)
.build();
parameters.add(inviteParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("removeTrackedInviteLinks")
.module(InviteFilterModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.messageCommandOnly(true)
.requiresConfirmation(true)
.supportsEmbedException(true)
.causesReaction(true)

View File

@@ -61,13 +61,21 @@ public class ShowTrackedInviteLinks extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("amount").type(Integer.class).optional(true).templated(true).build());
Parameter amountParameter = Parameter
.builder()
.name("amount")
.type(Integer.class)
.optional(true)
.templated(true)
.build();
parameters.add(amountParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("showTrackedInviteLinks")
.module(InviteFilterModerationModuleDefinition.MODERATION)
.templated(true)
.async(true)
.messageCommandOnly(true)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>invite-filter</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>link-embed</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>link-embed</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>logging</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.8</version>
<version>1.5.13</version>
</parent>
<modelVersion>4.0.0</modelVersion>

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