mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-11 03:38:51 +00:00
Compare commits
15 Commits
abstracto-
...
abstracto-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
650d062808 | ||
|
|
bac9832819 | ||
|
|
efbcb5c84b | ||
|
|
fd70e6ac90 | ||
|
|
29bde70796 | ||
|
|
ecd4feabb2 | ||
|
|
abf60409f1 | ||
|
|
080733957f | ||
|
|
8a41f366ae | ||
|
|
dbf478c44c | ||
|
|
3df688571f | ||
|
|
ca530949c6 | ||
|
|
724930e5a4 | ||
|
|
2875da117a | ||
|
|
f95ba6c28f |
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Sheldan
|
||||
Copyright (c) 2023 Sheldan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
10
README.md
10
README.md
@@ -14,22 +14,20 @@ 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-alpha.21
|
||||
* [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)
|
||||
* [JDA](https://github.com/DV8FromTheWorld/JDA/) The Discord API Wrapper in the version 5.0.0-beta.5
|
||||
* [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.
|
||||
* [Ehcache](https://github.com/ehcache/ehcache3) is used as a caching implementation.
|
||||
* [Lombok](https://github.com/rzwitserloot/lombok) is used as a framework in order to speed up creation of container classes and builders.
|
||||
* [Quartz](https://github.com/quartz-scheduler/quartz) is used as a scheduling framework in order to provide functionalities which either require a delayed or cronjob behaviour.
|
||||
* [Docker](https://github.com/docker) is used to package the application into a container and [Docker Compose](https://github.com/docker/compose) is used to connect the required containers together.
|
||||
* [Quartz](https://github.com/quartz-scheduler/quartz) is used as a scheduling framework in order to provide functionalities which either require a scheduled or cronjob behaviour.
|
||||
* [Docker](https://github.com/docker) is used to package the application into an image and [Docker Compose](https://github.com/docker/compose) is used to build the images
|
||||
* [Liquibase](https://github.com/liquibase/liquibase) is used to manage changes to the database
|
||||
|
||||
## Documentation
|
||||
A detailed documentation of the pure form of Abstracto including the terminology and commands in HTML form is available [here](https://sheldan.github.io/abstracto-docs/current). The PDF is available [here](https://sheldan.github.io/abstracto-docs/current/documentation.pdf)
|
||||
If you want to view the documentation to an earlier released version you need to append the desired version to the URL. The current version will be available aforementioned URL, but it is not right now, because Abstracto has not been released yet.
|
||||
|
||||
## Customization documentation
|
||||
TBD when Abstracto is released, as the current version is still being adapted, because of findings from the example customization in Crimson.
|
||||
|
||||
## Issues
|
||||
If you find any issue, feel free to create a GitHub issue.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>anti-raid</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>anti-raid</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>assignable-roles</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>assignable-roles</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>assignable-roles-int</artifactId>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>custom-command</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>custom-command</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>dynamic-activity</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>dynamic-activity</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>entertainment</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>entertainment</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>experience-tracking</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>experience-tracking</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>invite-filter</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>invite-filter</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>link-embed</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>link-embed</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>logging</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>logging</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>moderation</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>moderation</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>modmail</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberNameDisplay;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
@@ -81,6 +82,7 @@ public class Contact extends AbstractConditionableCommand {
|
||||
ModMailThreadExistsModel model = ModMailThreadExistsModel
|
||||
.builder()
|
||||
.existingModMailThread(existingThread)
|
||||
.executingMemberDisplay(MemberNameDisplay.fromMember(commandContext.getAuthor()))
|
||||
.build();
|
||||
List<CompletableFuture<Message>> futures = channelService.sendEmbedTemplateInTextChannelList(MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE, model, commandContext.getChannel());
|
||||
return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromIgnored());
|
||||
@@ -106,6 +108,7 @@ public class Contact extends AbstractConditionableCommand {
|
||||
ModMailThreadExistsModel model = ModMailThreadExistsModel
|
||||
.builder()
|
||||
.existingModMailThread(existingThread)
|
||||
.executingMemberDisplay(MemberNameDisplay.fromMember(event.getMember()))
|
||||
.build();
|
||||
return interactionService.replyEmbed(MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE, model, event)
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>modmail</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.sheldan.abstracto.modmail.model.template;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberNameDisplay;
|
||||
import dev.sheldan.abstracto.core.utils.ChannelUtils;
|
||||
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
|
||||
import lombok.Builder;
|
||||
@@ -15,6 +16,7 @@ import lombok.Setter;
|
||||
@Builder
|
||||
public class ModMailThreadExistsModel {
|
||||
private ModMailThread existingModMailThread;
|
||||
private MemberNameDisplay executingMemberDisplay;
|
||||
|
||||
public String getThreadUrl() {
|
||||
return ChannelUtils.buildChannelUrl(existingModMailThread.getServer().getId(), existingModMailThread.getChannel().getId());
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto</groupId>
|
||||
<artifactId>abstracto-application</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>profanity-filter</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>profanity-filter</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>remind</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>remind</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>repost-detection</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>repost-detection</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>starboard</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ public class StarboardServiceBean implements StarboardService {
|
||||
public void persistPost(CachedMessage message, List<Long> userExceptAuthorIds, List<CompletableFuture<Message>> completableFutures, Long starboardChannelId, Long starredUserId, Long userReactingId) {
|
||||
AUserInAServer innerStarredUser = userInServerManagementService.loadUserOptional(starredUserId).orElseThrow(() -> new UserInServerNotFoundException(starredUserId));
|
||||
AChannel starboardChannel = channelManagementService.loadChannel(starboardChannelId);
|
||||
Message starboardMessage = completableFutures.get(0).join();
|
||||
Message starboardMessage = completableFutures.get(0).join(); // TODO null pointer if post target is disabled
|
||||
AServerAChannelMessage aServerAChannelMessage = AServerAChannelMessage
|
||||
.builder()
|
||||
.messageId(starboardMessage.getIdLong())
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>starboard</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>statistic</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.exception.UploadFileTooLargeException;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
@@ -25,8 +24,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
@@ -46,8 +43,6 @@ import java.util.concurrent.CompletableFuture;
|
||||
public class ExportEmoteStats extends AbstractConditionableCommand {
|
||||
|
||||
public static final String DOWNLOAD_EMOTE_STATS_NO_STATS_AVAILABLE_RESPONSE_TEMPLATE_KEY = "downloadEmoteStats_no_stats_available_response";
|
||||
public static final String DOWNLOAD_EMOTE_STATS_FILE_NAME_TEMPLATE_KEY = "downloadEmoteStats_file_name";
|
||||
public static final String DOWNLOAD_EMOTE_STATS_FILE_CONTENT_TEMPLATE_KEY = "downloadEmoteStats_file_content";
|
||||
public static final String DOWNLOAD_EMOTE_STATS_RESPONSE_TEMPLATE_KEY = "downloadEmoteStats_response";
|
||||
@Autowired
|
||||
private ServerManagementService serverManagementService;
|
||||
@@ -93,30 +88,10 @@ public class ExportEmoteStats extends AbstractConditionableCommand {
|
||||
.requester(commandContext.getAuthor())
|
||||
.statsSince(toUseForModel)
|
||||
.build();
|
||||
String fileName = templateService.renderTemplate(DOWNLOAD_EMOTE_STATS_FILE_NAME_TEMPLATE_KEY, model);
|
||||
String fileContent = templateService.renderTemplate(DOWNLOAD_EMOTE_STATS_FILE_CONTENT_TEMPLATE_KEY, model);
|
||||
model.setEmoteStatsFileName(fileName);
|
||||
File tempFile = fileService.createTempFile(fileName);
|
||||
try {
|
||||
fileService.writeContentToFile(tempFile, fileContent);
|
||||
long maxFileSize = commandContext.getGuild().getMaxFileSize();
|
||||
// in this case, we cannot upload the file, so we need to fail
|
||||
if(tempFile.length() > maxFileSize) {
|
||||
throw new UploadFileTooLargeException(tempFile.length(), maxFileSize);
|
||||
}
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(DOWNLOAD_EMOTE_STATS_RESPONSE_TEMPLATE_KEY, model, actualServer.getId());
|
||||
messageToSend.getAttachedFiles().get(0).setFile(tempFile.getAbsoluteFile());
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenApply(unused -> CommandResult.fromIgnored());
|
||||
} catch (IOException e) {
|
||||
throw new AbstractoRunTimeException(e);
|
||||
} finally {
|
||||
try {
|
||||
fileService.safeDelete(tempFile);
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to delete temporary export emote statistics file {}.", tempFile.getAbsoluteFile(), e);
|
||||
}
|
||||
}
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(DOWNLOAD_EMOTE_STATS_RESPONSE_TEMPLATE_KEY, model, actualServer.getId());
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenAccept(unused -> fileService.safeDeleteIgnoreException(messageToSend.getAttachedFiles().get(0).getFile()))
|
||||
.thenApply(unused -> CommandResult.fromIgnored());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,8 +2,6 @@ package dev.sheldan.abstracto.statistic.emote.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.exception.UploadFileTooLargeException;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
|
||||
@@ -14,7 +12,6 @@ import dev.sheldan.abstracto.statistic.config.StatisticFeatureDefinition;
|
||||
import dev.sheldan.abstracto.statistic.emote.model.DownloadEmoteStatsModel;
|
||||
import dev.sheldan.abstracto.statistic.emote.model.database.UsedEmote;
|
||||
import dev.sheldan.abstracto.statistic.emote.service.management.UsedEmoteManagementService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
@@ -22,11 +19,8 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.*;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@@ -77,53 +71,11 @@ public class ExportEmoteStatsTest {
|
||||
verify(channelService, times(1)).sendEmbedTemplateInTextChannelList(eq(DOWNLOAD_EMOTE_STATS_NO_STATS_AVAILABLE_RESPONSE_TEMPLATE_KEY), any(), eq(commandContext.getChannel()));
|
||||
}
|
||||
|
||||
@Test(expected = AbstractoRunTimeException.class)
|
||||
public void testFileIOException() throws IOException {
|
||||
CommandContext commandContext = CommandTestUtilities.getNoParameters();
|
||||
mockServerAndFileRendering(commandContext);
|
||||
File file = Mockito.mock(File.class);
|
||||
when(fileService.createTempFile(FILE_NAME)).thenReturn(file);
|
||||
doThrow(new IOException()).when(fileService).writeContentToFile(file, FILE_CONTENT);
|
||||
testUnit.executeAsync(commandContext);
|
||||
}
|
||||
|
||||
@Test(expected = UploadFileTooLargeException.class)
|
||||
public void testExportAllEmoteStatsTooBig() throws IOException {
|
||||
CommandContext commandContext = CommandTestUtilities.getNoParameters();
|
||||
when(commandContext.getGuild().getMaxFileSize()).thenReturn(2L);
|
||||
mockServerAndFileRendering(commandContext);
|
||||
File file = Mockito.mock(File.class);
|
||||
when(fileService.createTempFile(FILE_NAME)).thenReturn(file);
|
||||
when(file.length()).thenReturn(3L);
|
||||
MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
|
||||
CompletableFuture<CommandResult> asyncResult = testUnit.executeAsync(commandContext);
|
||||
CommandTestUtilities.checkSuccessfulCompletionAsync(asyncResult);
|
||||
verify(fileService, times(1)).writeContentToFile(file, FILE_CONTENT);
|
||||
verify(fileService, times(1)).safeDelete(file);
|
||||
verifyModel();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFeature() {
|
||||
Assert.assertEquals(StatisticFeatureDefinition.EMOTE_TRACKING, testUnit.getFeature());
|
||||
}
|
||||
|
||||
private void verifyModel() {
|
||||
DownloadEmoteStatsModel model = modelArgumentCaptor.getValue();
|
||||
Assert.assertEquals(1, model.getEmotes().size());
|
||||
Assert.assertEquals(usedEmote, model.getEmotes().get(0));
|
||||
}
|
||||
|
||||
private void mockServerAndFileRendering(CommandContext commandContext) {
|
||||
when(commandContext.getGuild().getIdLong()).thenReturn(SERVER_ID);
|
||||
AServer server = Mockito.mock(AServer.class);
|
||||
when(serverManagementService.loadServer(SERVER_ID)).thenReturn(server);
|
||||
List<UsedEmote> usedEmotes = Arrays.asList(usedEmote);
|
||||
when(usedEmoteManagementService.loadEmoteUsagesForServerSince(server, Instant.EPOCH)).thenReturn(usedEmotes);
|
||||
when(templateService.renderTemplate(eq(DOWNLOAD_EMOTE_STATS_FILE_NAME_TEMPLATE_KEY), modelArgumentCaptor.capture())).thenReturn(FILE_NAME);
|
||||
when(templateService.renderTemplate(eq(DOWNLOAD_EMOTE_STATS_FILE_CONTENT_TEMPLATE_KEY), any())).thenReturn(FILE_CONTENT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateCommand() {
|
||||
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>statistic</artifactId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>suggestion</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package dev.sheldan.abstracto.suggestion.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||
import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
import dev.sheldan.abstracto.core.command.config.ParameterValidator;
|
||||
import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator;
|
||||
import dev.sheldan.abstracto.core.command.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.suggestion.config.SuggestionFeatureDefinition;
|
||||
import dev.sheldan.abstracto.suggestion.config.SuggestionSlashCommandNames;
|
||||
import dev.sheldan.abstracto.suggestion.service.PollService;
|
||||
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 CancelPoll extends AbstractConditionableCommand {
|
||||
|
||||
private static final String CANCEL_POLL_COMMAND = "cancelPoll";
|
||||
private static final String POLL_ID_PARAMETER = "pollId";
|
||||
private static final String CANCEL_POLL_RESPONSE = "cancelPoll_response";
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private PollService pollService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
Long pollId = slashCommandParameterService.getCommandOption(POLL_ID_PARAMETER, event, Integer.class).longValue();
|
||||
return pollService.cancelPoll(pollId, event.getGuild().getIdLong(), event.getMember())
|
||||
.thenCompose(unused -> interactionService.replyEmbed(CANCEL_POLL_RESPONSE, event))
|
||||
.thenApply(aVoid -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return SuggestionFeatureDefinition.POLL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<ParameterValidator> pollIdValidator = Arrays.asList(MinIntegerValueValidator.min(1L));
|
||||
Parameter pollIdParameter = Parameter
|
||||
.builder()
|
||||
.name(POLL_ID_PARAMETER)
|
||||
.validators(pollIdValidator)
|
||||
.type(Long.class)
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
List<Parameter> parameters = Arrays.asList(pollIdParameter);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(SuggestionSlashCommandNames.POLL_PUBLIC)
|
||||
.commandName("cancel")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(CANCEL_POLL_COMMAND)
|
||||
.module(UtilityModuleDefinition.UTILITY)
|
||||
.templated(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.async(true)
|
||||
.supportsEmbedException(true)
|
||||
.causesReaction(true)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package dev.sheldan.abstracto.suggestion.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||
import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
import dev.sheldan.abstracto.core.command.config.ParameterValidator;
|
||||
import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator;
|
||||
import dev.sheldan.abstracto.core.command.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.suggestion.config.SuggestionFeatureDefinition;
|
||||
import dev.sheldan.abstracto.suggestion.config.SuggestionSlashCommandNames;
|
||||
import dev.sheldan.abstracto.suggestion.service.PollService;
|
||||
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 ClosePoll extends AbstractConditionableCommand {
|
||||
|
||||
private static final String CLOSE_POLL_COMMAND = "closePoll";
|
||||
private static final String POLL_ID_PARAMETER = "pollId";
|
||||
private static final String TEXT_PARAMETER = "text";
|
||||
private static final String CLOSE_POLL_RESPONSE = "closePoll_response";
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private PollService pollService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
Long pollId = slashCommandParameterService.getCommandOption(POLL_ID_PARAMETER, event, Integer.class).longValue();
|
||||
String text;
|
||||
if(slashCommandParameterService.hasCommandOption(TEXT_PARAMETER, event)) {
|
||||
text = slashCommandParameterService.getCommandOption(TEXT_PARAMETER, event, String.class);
|
||||
} else {
|
||||
text = "";
|
||||
}
|
||||
return pollService.closePoll(pollId, event.getGuild().getIdLong(), text, event.getMember())
|
||||
.thenCompose(unused -> interactionService.replyEmbed(CLOSE_POLL_RESPONSE, event))
|
||||
.thenApply(aVoid -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return SuggestionFeatureDefinition.POLL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<ParameterValidator> pollIdValidator = Arrays.asList(MinIntegerValueValidator.min(1L));
|
||||
Parameter pollIdParameter = Parameter
|
||||
.builder()
|
||||
.name(POLL_ID_PARAMETER)
|
||||
.validators(pollIdValidator)
|
||||
.type(Long.class)
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
Parameter textParameter = Parameter
|
||||
.builder()
|
||||
.name(TEXT_PARAMETER)
|
||||
.type(String.class)
|
||||
.optional(true)
|
||||
.remainder(true)
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
List<Parameter> parameters = Arrays.asList(pollIdParameter, textParameter);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(SuggestionSlashCommandNames.POLL)
|
||||
.commandName("close")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(CLOSE_POLL_COMMAND)
|
||||
.module(UtilityModuleDefinition.UTILITY)
|
||||
.templated(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.async(true)
|
||||
.supportsEmbedException(true)
|
||||
.causesReaction(true)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package dev.sheldan.abstracto.suggestion.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.suggestion.config.SuggestionFeatureDefinition;
|
||||
import dev.sheldan.abstracto.suggestion.config.SuggestionSlashCommandNames;
|
||||
import dev.sheldan.abstracto.suggestion.service.PollService;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class Poll extends AbstractConditionableCommand {
|
||||
|
||||
private static final String POLL_COMMAND = "poll";
|
||||
private static final String ALLOW_MULTIPLE_PARAMETER = "allowMultiple";
|
||||
private static final String SHOW_DECISIONS_PARAMETER = "showDecisions";
|
||||
private static final String ALLOW_ADDITIONS_PARAMETER = "allowAdditions";
|
||||
private static final String POLL_DURATION_PARAMETER = "pollDuration";
|
||||
private static final String POLL_DESCRIPTION_PARAMETER = "description";
|
||||
private static final String POLL_OPTIONS_PARAMETER = "options";
|
||||
private static final Integer OPTIONS_COUNT = 15;
|
||||
private static final String POLL_RESPONSE_TEMPLATE_KEY = "poll_server_response";
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private PollService pollService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
List<String> options = new ArrayList<>();
|
||||
for (int i = 0; i < OPTIONS_COUNT; i++) {
|
||||
if(slashCommandParameterService.hasCommandOption(POLL_OPTIONS_PARAMETER + "_" + i, event)) {
|
||||
String choice = slashCommandParameterService.getCommandOption(POLL_OPTIONS_PARAMETER + "_" + i, event, String.class);
|
||||
options.add(choice);
|
||||
}
|
||||
}
|
||||
Boolean allowMultiple = false;
|
||||
if(slashCommandParameterService.hasCommandOption(ALLOW_MULTIPLE_PARAMETER, event)) {
|
||||
allowMultiple = slashCommandParameterService.getCommandOption(ALLOW_MULTIPLE_PARAMETER, event, Boolean.class);
|
||||
}
|
||||
|
||||
Boolean showDecisions = false;
|
||||
if(slashCommandParameterService.hasCommandOption(SHOW_DECISIONS_PARAMETER, event)) {
|
||||
showDecisions = slashCommandParameterService.getCommandOption(SHOW_DECISIONS_PARAMETER, event, Boolean.class);
|
||||
}
|
||||
|
||||
Boolean allowAdditions = false;
|
||||
if(slashCommandParameterService.hasCommandOption(ALLOW_ADDITIONS_PARAMETER, event)) {
|
||||
allowAdditions = slashCommandParameterService.getCommandOption(ALLOW_ADDITIONS_PARAMETER, event, Boolean.class);
|
||||
}
|
||||
Duration pollDuration = null;
|
||||
if(slashCommandParameterService.hasCommandOption(POLL_DURATION_PARAMETER, event)) {
|
||||
String durationString = slashCommandParameterService.getCommandOption(POLL_DURATION_PARAMETER, event, Duration.class, String.class);
|
||||
pollDuration = ParseUtils.parseDuration(durationString);
|
||||
}
|
||||
Boolean actualMultiple = allowMultiple;
|
||||
Boolean actualDecisions = showDecisions;
|
||||
Boolean actualAdditions = allowAdditions;
|
||||
Duration actualDuration = pollDuration;
|
||||
String description = slashCommandParameterService.getCommandOption(POLL_DESCRIPTION_PARAMETER, event, String.class);
|
||||
return event.deferReply()
|
||||
.submit()
|
||||
.thenCompose(interactionHook -> pollService.createServerPoll(event.getMember(), options, description, actualMultiple, actualAdditions, actualDecisions, actualDuration)
|
||||
.thenAccept(unused -> interactionService.sendMessageToInteraction(POLL_RESPONSE_TEMPLATE_KEY, new Object(), interactionHook)))
|
||||
.thenApply(unused -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
|
||||
Parameter allowMultipleParameter = Parameter
|
||||
.builder()
|
||||
.name(ALLOW_MULTIPLE_PARAMETER)
|
||||
.type(Boolean.class)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
Parameter showDecisions = Parameter
|
||||
.builder()
|
||||
.name(SHOW_DECISIONS_PARAMETER)
|
||||
.type(Boolean.class)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
Parameter allowAdditions = Parameter
|
||||
.builder()
|
||||
.name(ALLOW_ADDITIONS_PARAMETER)
|
||||
.type(Boolean.class)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
Parameter description = Parameter
|
||||
.builder()
|
||||
.name(POLL_DESCRIPTION_PARAMETER)
|
||||
.type(String.class)
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
Parameter duration = Parameter
|
||||
.builder()
|
||||
.name(POLL_DURATION_PARAMETER)
|
||||
.type(Duration.class)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
Parameter optionsParameter = Parameter
|
||||
.builder()
|
||||
.name(POLL_OPTIONS_PARAMETER)
|
||||
.type(String.class)
|
||||
.templated(true)
|
||||
.remainder(true)
|
||||
.listSize(OPTIONS_COUNT)
|
||||
.isListParam(true)
|
||||
.build();
|
||||
|
||||
|
||||
List<Parameter> parameters = Arrays.asList(description, optionsParameter, allowMultipleParameter, showDecisions, allowAdditions, duration);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(SuggestionSlashCommandNames.POLL_PUBLIC)
|
||||
.commandName("server")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(POLL_COMMAND)
|
||||
.module(UtilityModuleDefinition.UTILITY)
|
||||
.templated(true)
|
||||
.async(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.supportsEmbedException(true)
|
||||
.causesReaction(false)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return SuggestionFeatureDefinition.POLL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package dev.sheldan.abstracto.suggestion.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.slash.SlashCommandConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
import dev.sheldan.abstracto.core.utils.ParseUtils;
|
||||
import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureDefinition;
|
||||
import dev.sheldan.abstracto.suggestion.config.SuggestionSlashCommandNames;
|
||||
import dev.sheldan.abstracto.suggestion.service.PollService;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class QuickPoll extends AbstractConditionableCommand {
|
||||
|
||||
private static final String POLL_COMMAND = "quickPoll";
|
||||
private static final String ALLOW_MULTIPLE_PARAMETER = "allowMultiple";
|
||||
private static final String POLL_DURATION_PARAMETER = "pollDuration";
|
||||
private static final String SHOW_DECISIONS_PARAMETER = "showDecisions";
|
||||
private static final String POLL_DESCRIPTION_PARAMETER = "description";
|
||||
private static final String POLL_OPTIONS_PARAMETER = "options";
|
||||
private static final Integer OPTIONS_COUNT = 15;
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Autowired
|
||||
private PollService pollService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
List<String> options = new ArrayList<>();
|
||||
for (int i = 0; i < OPTIONS_COUNT; i++) {
|
||||
if(slashCommandParameterService.hasCommandOption(POLL_OPTIONS_PARAMETER + "_" + i, event)) {
|
||||
String choice = slashCommandParameterService.getCommandOption(POLL_OPTIONS_PARAMETER + "_" + i, event, String.class);
|
||||
options.add(choice);
|
||||
}
|
||||
}
|
||||
|
||||
Boolean allowMultiple = false;
|
||||
if(slashCommandParameterService.hasCommandOption(ALLOW_MULTIPLE_PARAMETER, event)) {
|
||||
allowMultiple = slashCommandParameterService.getCommandOption(ALLOW_MULTIPLE_PARAMETER, event, Boolean.class);
|
||||
}
|
||||
|
||||
Boolean showDecisions = false;
|
||||
if(slashCommandParameterService.hasCommandOption(SHOW_DECISIONS_PARAMETER, event)) {
|
||||
showDecisions = slashCommandParameterService.getCommandOption(SHOW_DECISIONS_PARAMETER, event, Boolean.class);
|
||||
}
|
||||
|
||||
Duration pollDuration = null;
|
||||
if(slashCommandParameterService.hasCommandOption(POLL_DURATION_PARAMETER, event)) {
|
||||
String durationString = slashCommandParameterService.getCommandOption(POLL_DURATION_PARAMETER, event, Duration.class, String.class);
|
||||
pollDuration = ParseUtils.parseDuration(durationString);
|
||||
}
|
||||
|
||||
Boolean actualMultiple = allowMultiple;
|
||||
Boolean actualShowDecisions = showDecisions;
|
||||
Duration actualDuration = pollDuration;
|
||||
String description = slashCommandParameterService.getCommandOption(POLL_DESCRIPTION_PARAMETER, event, String.class);
|
||||
|
||||
return event.deferReply()
|
||||
.submit()
|
||||
.thenCompose(interactionHook -> pollService.createQuickPoll(event.getMember(), options, description, actualMultiple, actualShowDecisions, interactionHook, actualDuration))
|
||||
.thenApply(unused -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
|
||||
Parameter allowMultipleParameter = Parameter
|
||||
.builder()
|
||||
.name(ALLOW_MULTIPLE_PARAMETER)
|
||||
.type(Boolean.class)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
Parameter description = Parameter
|
||||
.builder()
|
||||
.name(POLL_DESCRIPTION_PARAMETER)
|
||||
.type(String.class)
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
|
||||
Parameter showDecisions = Parameter
|
||||
.builder()
|
||||
.name(SHOW_DECISIONS_PARAMETER)
|
||||
.type(Boolean.class)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
|
||||
Parameter duration = Parameter
|
||||
.builder()
|
||||
.name(POLL_DURATION_PARAMETER)
|
||||
.type(Duration.class)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
Parameter optionsParameter = Parameter
|
||||
.builder()
|
||||
.name(POLL_OPTIONS_PARAMETER)
|
||||
.type(String.class)
|
||||
.templated(true)
|
||||
.remainder(true)
|
||||
.listSize(OPTIONS_COUNT)
|
||||
.isListParam(true)
|
||||
.build();
|
||||
|
||||
|
||||
List<Parameter> parameters = Arrays.asList(description, optionsParameter, allowMultipleParameter, showDecisions, duration);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(SuggestionSlashCommandNames.POLL_PUBLIC)
|
||||
.commandName("quick")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(POLL_COMMAND)
|
||||
.module(UtilityModuleDefinition.UTILITY)
|
||||
.templated(true)
|
||||
.async(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.supportsEmbedException(true)
|
||||
.causesReaction(false)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return SuggestionFeatureDefinition.POLL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package dev.sheldan.abstracto.suggestion.job;
|
||||
|
||||
import dev.sheldan.abstracto.suggestion.service.PollService;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.DisallowConcurrentExecution;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
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
|
||||
@Getter
|
||||
@Setter
|
||||
public class QuickPollEvaluationJob extends QuartzJobBean {
|
||||
|
||||
private Long pollId;
|
||||
private Long serverId;
|
||||
|
||||
@Autowired
|
||||
private PollService pollService;
|
||||
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
|
||||
log.info("Executing poll evaluation job for quick poll {} in server {}.", pollId, serverId);
|
||||
try {
|
||||
pollService.evaluateQuickPoll(pollId, serverId).thenAccept(unused -> {
|
||||
log.info("Evaluated quick poll {} in server {}.", pollId, serverId);
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to evaluate quick poll {} in server {}.", pollId, serverId, throwable);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception exception) {
|
||||
log.error("Quick poll evaluation job failed.", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package dev.sheldan.abstracto.suggestion.job;
|
||||
|
||||
import dev.sheldan.abstracto.suggestion.service.PollService;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.DisallowConcurrentExecution;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
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
|
||||
@Getter
|
||||
@Setter
|
||||
public class ServerPollEvaluationJob extends QuartzJobBean {
|
||||
|
||||
private Long pollId;
|
||||
private Long serverId;
|
||||
|
||||
@Autowired
|
||||
private PollService pollService;
|
||||
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
|
||||
log.info("Executing poll evaluation job for server poll {} in server {}.", pollId, serverId);
|
||||
try {
|
||||
pollService.evaluateServerPoll(pollId, serverId).thenAccept(unused -> {
|
||||
log.info("Evaluated server poll {} in server {}.", pollId, serverId);
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to evaluate server poll {} in server {}.", pollId, serverId, throwable);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception exception) {
|
||||
log.error("Server poll evaluation job failed.", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package dev.sheldan.abstracto.suggestion.job;
|
||||
|
||||
import dev.sheldan.abstracto.suggestion.service.PollService;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.DisallowConcurrentExecution;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
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
|
||||
@Getter
|
||||
@Setter
|
||||
public class ServerPollReminderJob extends QuartzJobBean {
|
||||
|
||||
private Long pollId;
|
||||
private Long serverId;
|
||||
|
||||
@Autowired
|
||||
private PollService pollService;
|
||||
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
|
||||
log.info("Executing server poll reminder job for server poll {} in server {}.", pollId, serverId);
|
||||
try {
|
||||
pollService.remindServerPoll(pollId, serverId).thenAccept(unused -> {
|
||||
log.info("Evaluated server poll {} in server {}.", pollId, serverId);
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to evaluate server poll {} in server {}.", pollId, serverId, throwable);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception exception) {
|
||||
log.error("Server poll evaluation job failed.", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package dev.sheldan.abstracto.suggestion.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.menu.listener.StringSelectMenuListener;
|
||||
import dev.sheldan.abstracto.core.interaction.menu.listener.StringSelectMenuListenerModel;
|
||||
import dev.sheldan.abstracto.core.interaction.menu.listener.StringSelectMenuListenerResult;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberNameDisplay;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureDefinition;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollType;
|
||||
import dev.sheldan.abstracto.suggestion.model.payload.QuickPollSelectionMenuPayload;
|
||||
import dev.sheldan.abstracto.suggestion.model.template.PollDecisionNotificationModel;
|
||||
import dev.sheldan.abstracto.suggestion.service.PollService;
|
||||
import dev.sheldan.abstracto.suggestion.service.PollServiceBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class QuickPollDecisionListener implements StringSelectMenuListener {
|
||||
|
||||
@Autowired
|
||||
private PollService pollService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
private static final String POLL_DECISION_NOTIFICATION = "poll_quick_decision_notification";
|
||||
|
||||
@Override
|
||||
public StringSelectMenuListenerResult execute(StringSelectMenuListenerModel model) {
|
||||
StringSelectInteractionEvent event = model.getEvent();
|
||||
QuickPollSelectionMenuPayload payload = (QuickPollSelectionMenuPayload) model.getDeserializedPayload();
|
||||
PollDecisionNotificationModel notificationModel = PollDecisionNotificationModel
|
||||
.builder()
|
||||
.chosenValues(event.getValues())
|
||||
.pollId(payload.getPollId())
|
||||
.memberNameDisplay(MemberNameDisplay.fromMember(event.getMember()))
|
||||
.serverId(model.getServerId())
|
||||
.build();
|
||||
pollService.setDecisionsInPollTo(event.getMember(), event.getValues(), payload.getPollId(), PollType.QUICK)
|
||||
.thenCompose(unused -> FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(POLL_DECISION_NOTIFICATION, notificationModel, event.getInteraction().getHook())))
|
||||
.exceptionally(throwable -> {
|
||||
log.info("Failed to member {} in server {} about decision in poll {}.", event.getMember().getIdLong(), model.getServerId(), payload.getPollId(), throwable);
|
||||
return null;
|
||||
}).thenAccept(unused1 -> {
|
||||
log.info("Notified member {} in server {} about decision in poll {}.", event.getMember().getIdLong(), model.getServerId(), payload.getPollId());
|
||||
});
|
||||
return StringSelectMenuListenerResult.ACKNOWLEDGED;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean handlesEvent(StringSelectMenuListenerModel model) {
|
||||
return model.getOrigin().equals(PollServiceBean.QUICK_POLL_SELECTION_MENU_ORIGIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return SuggestionFeatureDefinition.POLL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPriority() {
|
||||
return ListenerPriority.MEDIUM;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package dev.sheldan.abstracto.suggestion.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.config.ListenerPriority;
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentPayloadManagementService;
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentService;
|
||||
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.interaction.modal.ModalConfigPayload;
|
||||
import dev.sheldan.abstracto.core.interaction.modal.ModalService;
|
||||
import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureDefinition;
|
||||
import dev.sheldan.abstracto.suggestion.model.payload.PollAddOptionButtonPayload;
|
||||
import dev.sheldan.abstracto.suggestion.model.template.PollAddOptionModalModel;
|
||||
import dev.sheldan.abstracto.suggestion.model.payload.PollAddOptionModalPayload;
|
||||
import dev.sheldan.abstracto.suggestion.service.PollServiceBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ServerPollAddOptionButtonListener implements ButtonClickedListener {
|
||||
|
||||
@Autowired
|
||||
private ComponentService componentService;
|
||||
|
||||
@Autowired
|
||||
private ModalService modalService;
|
||||
|
||||
@Autowired
|
||||
private ServerPollAddOptionButtonListener self;
|
||||
|
||||
@Autowired
|
||||
private ComponentPayloadManagementService componentPayloadManagementService;
|
||||
|
||||
private static final String SERVER_POLL_ADD_OPTION_MODAL_TEMPLATE = "poll_add_option";
|
||||
public static final String SERVER_POLL_ADD_OPTION_MODAL_ORIGIN = "SERVER_POLL_ADD_OPTION_MODAL";
|
||||
|
||||
@Override
|
||||
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
|
||||
PollAddOptionButtonPayload payload = (PollAddOptionButtonPayload) model.getDeserializedPayload();
|
||||
String modalId = componentService.generateComponentId();
|
||||
String labelInputId = componentService.generateComponentId();
|
||||
String descriptionInputId = componentService.generateComponentId();
|
||||
PollAddOptionModalModel modalModel = PollAddOptionModalModel
|
||||
.builder()
|
||||
.descriptionInputComponentId(descriptionInputId)
|
||||
.modalId(modalId)
|
||||
.labelInputComponentId(labelInputId)
|
||||
.build();
|
||||
modalService.replyModal(model.getEvent(), SERVER_POLL_ADD_OPTION_MODAL_TEMPLATE, modalModel).thenAccept(unused -> {
|
||||
log.info("Opened a model for entering a new option for poll {} in server {} for user {}.",
|
||||
payload.getPollId(), payload.getServerId(), model.getEvent().getMember().getIdLong());
|
||||
self.persistModalPayload(modalModel, model.getServerId(), payload.getPollId());
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to show modal for entering a new option for poll {} in server {} for user {}.",
|
||||
payload.getPollId(), payload.getServerId(), model.getEvent().getMember().getIdLong(), throwable);
|
||||
return null;
|
||||
});
|
||||
return ButtonClickedListenerResult.ACKNOWLEDGED;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void persistModalPayload(PollAddOptionModalModel model, Long serverId, Long pollId) {
|
||||
PollAddOptionModalPayload payload = PollAddOptionModalPayload
|
||||
.builder()
|
||||
.modalId(model.getModalId())
|
||||
.labelInputComponentId(model.getLabelInputComponentId())
|
||||
.descriptionInputComponentId(model.getDescriptionInputComponentId())
|
||||
.serverId(serverId)
|
||||
.pollId(pollId)
|
||||
.build();
|
||||
ModalConfigPayload payloadConfig = ModalConfigPayload
|
||||
.builder()
|
||||
.modalPayload(payload)
|
||||
.origin(SERVER_POLL_ADD_OPTION_MODAL_ORIGIN)
|
||||
.payloadType(payload.getClass())
|
||||
.modalId(model.getModalId())
|
||||
.build();
|
||||
componentPayloadManagementService.createModalPayload(payloadConfig, serverId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean autoAcknowledgeEvent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean handlesEvent(ButtonClickedListenerModel model) {
|
||||
return PollServiceBean.SERVER_POLL_ADD_OPTION_ORIGIN.equals(model.getOrigin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return SuggestionFeatureDefinition.POLL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPriority() {
|
||||
return ListenerPriority.MEDIUM;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package dev.sheldan.abstracto.suggestion.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.modal.listener.ModalInteractionListener;
|
||||
import dev.sheldan.abstracto.core.interaction.modal.listener.ModalInteractionListenerModel;
|
||||
import dev.sheldan.abstracto.core.interaction.modal.listener.ModalInteractionListenerResult;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberNameDisplay;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureDefinition;
|
||||
import dev.sheldan.abstracto.suggestion.exception.PollOptionAlreadyExistsException;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.Poll;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollType;
|
||||
import dev.sheldan.abstracto.suggestion.model.payload.PollAddOptionModalPayload;
|
||||
import dev.sheldan.abstracto.suggestion.model.template.PollAddOptionNotificationModel;
|
||||
import dev.sheldan.abstracto.suggestion.service.PollService;
|
||||
import dev.sheldan.abstracto.suggestion.service.management.PollManagementService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.interactions.modals.ModalMapping;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ServerPollAddOptionModalListener implements ModalInteractionListener {
|
||||
|
||||
@Autowired
|
||||
private PollService pollService;
|
||||
|
||||
@Autowired
|
||||
private ServerPollAddOptionModalListener self;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private PollManagementService pollManagementService;
|
||||
|
||||
private static final String POLL_ADD_OPTION_NOTIFICATION = "poll_add_option_notification";
|
||||
|
||||
@Override
|
||||
public ModalInteractionListenerResult execute(ModalInteractionListenerModel model) {
|
||||
PollAddOptionModalPayload payload = (PollAddOptionModalPayload) model.getDeserializedPayload();
|
||||
log.info("Handling modal event to add options to poll {} in server {} by member {}.", payload.getPollId(), payload.getServerId(), model.getEvent().getMember().getIdLong());
|
||||
String labelContent = model
|
||||
.getEvent()
|
||||
.getValues()
|
||||
.stream()
|
||||
.filter(modalMapping -> modalMapping.getId().equals(payload.getLabelInputComponentId()))
|
||||
.map(ModalMapping::getAsString)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
Poll affectedPoll = pollManagementService.getPollByPollId(payload.getPollId(), payload.getServerId(), PollType.STANDARD);
|
||||
if(affectedPoll.getOptions().stream().anyMatch(pollOption -> pollOption.getLabel().equals(labelContent))) {
|
||||
throw new PollOptionAlreadyExistsException();
|
||||
}
|
||||
|
||||
String descriptionContent = model
|
||||
.getEvent()
|
||||
.getValues()
|
||||
.stream()
|
||||
.filter(modalMapping -> modalMapping.getId().equals(payload.getDescriptionInputComponentId()))
|
||||
.map(ModalMapping::getAsString)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
PollAddOptionNotificationModel pollAddOptionNotificationModel = PollAddOptionNotificationModel
|
||||
.builder()
|
||||
.description(descriptionContent)
|
||||
.memberNameDisplay(MemberNameDisplay.fromMember(model.getEvent().getMember()))
|
||||
.label(labelContent)
|
||||
.value(labelContent)
|
||||
.pollId(payload.getPollId())
|
||||
.serverId(payload.getServerId())
|
||||
.build();
|
||||
|
||||
|
||||
model.getEvent().deferReply(true).queue(interactionHook -> {
|
||||
self.updatePoll(model, payload, labelContent, descriptionContent);
|
||||
FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(POLL_ADD_OPTION_NOTIFICATION, pollAddOptionNotificationModel, model.getEvent().getInteraction().getHook())).thenAccept(unused -> {
|
||||
log.info("Send notification about successfully adding option to poll {} in server {} to member {}", payload.getPollId(), payload.getServerId(), model.getEvent().getMember().getIdLong());
|
||||
}).exceptionally(throwable -> {
|
||||
log.info("Failed to send notification about adding option to poll {} in server {} to member {}", payload.getPollId(), payload.getServerId(), model.getEvent().getMember().getIdLong());
|
||||
return null;
|
||||
});
|
||||
}, throwable -> {
|
||||
log.error("Failed to acknowledge modal interaction for poll add option modal listener in guild {}.", model.getServerId(), throwable);
|
||||
});
|
||||
|
||||
return ModalInteractionListenerResult.ACKNOWLEDGED;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void updatePoll(ModalInteractionListenerModel model, PollAddOptionModalPayload payload, String labelContent, String descriptionContent) {
|
||||
pollService.addOptionToServerPoll(payload.getPollId(), payload.getServerId(), model.getEvent().getMember(), labelContent, descriptionContent).thenAccept(unused -> {
|
||||
log.info("Added option to poll {} in server {} by member {}.", payload.getPollId(), payload.getServerId(), model.getEvent().getMember().getIdLong());
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to add option to poll {} in server {} by member {}.",
|
||||
payload.getPollId(), payload.getServerId(), model.getEvent().getMember().getIdLong(), throwable);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return SuggestionFeatureDefinition.POLL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPriority() {
|
||||
return ListenerPriority.MEDIUM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean handlesEvent(ModalInteractionListenerModel model) {
|
||||
return ServerPollAddOptionButtonListener.SERVER_POLL_ADD_OPTION_MODAL_ORIGIN.equals(model.getOrigin());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package dev.sheldan.abstracto.suggestion.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.menu.listener.StringSelectMenuListener;
|
||||
import dev.sheldan.abstracto.core.interaction.menu.listener.StringSelectMenuListenerModel;
|
||||
import dev.sheldan.abstracto.core.interaction.menu.listener.StringSelectMenuListenerResult;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberNameDisplay;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureDefinition;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollType;
|
||||
import dev.sheldan.abstracto.suggestion.model.template.PollDecisionNotificationModel;
|
||||
import dev.sheldan.abstracto.suggestion.model.payload.ServerPollSelectionMenuPayload;
|
||||
import dev.sheldan.abstracto.suggestion.service.PollService;
|
||||
import dev.sheldan.abstracto.suggestion.service.PollServiceBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ServerPollDecisionListener implements StringSelectMenuListener {
|
||||
|
||||
@Autowired
|
||||
private PollService pollService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
private static final String POLL_DECISION_NOTIFICATION = "poll_decision_notification";
|
||||
|
||||
@Override
|
||||
public StringSelectMenuListenerResult execute(StringSelectMenuListenerModel model) {
|
||||
StringSelectInteractionEvent event = model.getEvent();
|
||||
ServerPollSelectionMenuPayload payload = (ServerPollSelectionMenuPayload) model.getDeserializedPayload();
|
||||
PollDecisionNotificationModel notificationModel = PollDecisionNotificationModel
|
||||
.builder()
|
||||
.chosenValues(event.getValues())
|
||||
.pollId(payload.getPollId())
|
||||
.memberNameDisplay(MemberNameDisplay.fromMember(event.getMember()))
|
||||
.serverId(model.getServerId())
|
||||
.build();
|
||||
pollService.setDecisionsInPollTo(event.getMember(), event.getValues(), payload.getPollId(), PollType.STANDARD)
|
||||
.thenCompose(unused -> FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(POLL_DECISION_NOTIFICATION, notificationModel, event.getInteraction().getHook())))
|
||||
.exceptionally(throwable -> {
|
||||
log.info("Failed to member {} in server {} about decision in poll {}.", event.getMember().getIdLong(), model.getServerId(), payload.getPollId(), throwable);
|
||||
return null;
|
||||
}).thenAccept(unused1 -> {
|
||||
log.info("Notified member {} in server {} about decision in poll {}.", event.getMember().getIdLong(), model.getServerId(), payload.getPollId());
|
||||
});
|
||||
return StringSelectMenuListenerResult.ACKNOWLEDGED;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean handlesEvent(StringSelectMenuListenerModel model) {
|
||||
return model.getOrigin().equals(PollServiceBean.SERVER_POLL_SELECTION_MENU_ORIGIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return SuggestionFeatureDefinition.POLL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPriority() {
|
||||
return ListenerPriority.MEDIUM;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package dev.sheldan.abstracto.suggestion.repository;
|
||||
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollOption;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface PollOptionRepository extends JpaRepository<PollOption, Long> {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package dev.sheldan.abstracto.suggestion.repository;
|
||||
|
||||
import dev.sheldan.abstracto.suggestion.model.database.Poll;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollType;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface PollRepository extends JpaRepository<Poll, Long> {
|
||||
Optional<Poll> findByPollIdAndServer_IdAndType(Long pollId, Long serverId, PollType pollType);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package dev.sheldan.abstracto.suggestion.repository;
|
||||
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollUserDecisionOption;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface PollUserDecisionOptionRepository extends JpaRepository<PollUserDecisionOption, Long> {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.sheldan.abstracto.suggestion.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.Poll;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollUserDecision;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface PollUserDecisionRepository extends JpaRepository<PollUserDecision, Long> {
|
||||
Optional<PollUserDecision> findPollUserDecisionByPollAndVoter(Poll poll, AUserInAServer voter);
|
||||
}
|
||||
@@ -0,0 +1,567 @@
|
||||
package dev.sheldan.abstracto.suggestion.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentPayloadManagementService;
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentService;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.button.ButtonConfigModel;
|
||||
import dev.sheldan.abstracto.core.interaction.menu.SelectMenuConfigModel;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberNameDisplay;
|
||||
import dev.sheldan.abstracto.core.service.*;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.core.utils.MessageUtils;
|
||||
import dev.sheldan.abstracto.scheduling.model.JobParameters;
|
||||
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
|
||||
import dev.sheldan.abstracto.suggestion.config.PollFeatureMode;
|
||||
import dev.sheldan.abstracto.suggestion.config.PollPostTarget;
|
||||
import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureDefinition;
|
||||
import dev.sheldan.abstracto.suggestion.exception.PollCancellationNotPossibleException;
|
||||
import dev.sheldan.abstracto.suggestion.exception.PollOptionAlreadyExistsException;
|
||||
import dev.sheldan.abstracto.suggestion.model.payload.PollAddOptionButtonPayload;
|
||||
import dev.sheldan.abstracto.suggestion.model.PollCreationRequest;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.*;
|
||||
import dev.sheldan.abstracto.suggestion.model.payload.QuickPollSelectionMenuPayload;
|
||||
import dev.sheldan.abstracto.suggestion.model.template.*;
|
||||
import dev.sheldan.abstracto.suggestion.model.payload.ServerPollSelectionMenuPayload;
|
||||
import dev.sheldan.abstracto.suggestion.service.management.PollManagementService;
|
||||
import dev.sheldan.abstracto.suggestion.service.management.PollOptionManagementService;
|
||||
import dev.sheldan.abstracto.suggestion.service.management.PollUserDecisionManagementService;
|
||||
import dev.sheldan.abstracto.suggestion.service.management.PollUserDecisionOptionManagementService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class PollServiceBean implements PollService {
|
||||
|
||||
@Autowired
|
||||
private CounterService counterService;
|
||||
|
||||
@Autowired
|
||||
private PollManagementService pollManagementService;
|
||||
|
||||
@Autowired
|
||||
private PollOptionManagementService pollOptionManagementService;
|
||||
|
||||
@Autowired
|
||||
private PostTargetService postTargetService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private ComponentService componentService;
|
||||
|
||||
@Autowired
|
||||
private ComponentPayloadManagementService componentPayloadManagementService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private PollUserDecisionManagementService pollUserDecisionManagementService;
|
||||
|
||||
@Autowired
|
||||
private PollUserDecisionOptionManagementService pollUserDecisionOptionManagementService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private SchedulerService schedulerService;
|
||||
|
||||
@Autowired
|
||||
private ConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private FeatureModeService featureModeService;
|
||||
|
||||
@Autowired
|
||||
private MessageService messageService;
|
||||
|
||||
@Autowired
|
||||
private PollServiceBean self;
|
||||
|
||||
private static final String POLLS_COUNTER_KEY = "POLLS";
|
||||
public static final String SERVER_POLL_SELECTION_MENU_ORIGIN = "SERVER_POLL_SELECTION_MENU";
|
||||
public static final String SERVER_POLL_ADD_OPTION_ORIGIN = "SERVER_POLL_ADD_OPTION_BUTTON";
|
||||
private static final String SERVER_POLL_TEMPLATE_KEY = "poll_server_message";
|
||||
private static final String SERVER_POLL_CLOSE_MESSAGE = "poll_server_close_message";
|
||||
private static final String SERVER_POLL_REMINDER_TEMPLATE_KEY = "poll_server_reminder_message";
|
||||
private static final String SERVER_POLL_EVALUATION_UPDATE_TEMPLATE_KEY = "poll_server_evaluation_update_message";
|
||||
private static final String QUICK_POLLS_COUNTER_KEY = "QUICK_POLLS";
|
||||
public static final String QUICK_POLL_SELECTION_MENU_ORIGIN = "QUICK_POLL_SELECTION_MENU";
|
||||
private static final String QUICK_POLL_TEMPLATE_KEY = "poll_quick_message";
|
||||
private static final String QUICK_POLL_EVALUATION_UPDATE_TEMPLATE_KEY = "poll_quick_evaluation_update_message";
|
||||
|
||||
@Value("${abstracto.feature.poll.removalMaxAge}")
|
||||
private Long removalMaxAgeSeconds;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CompletableFuture<Void> createServerPoll(Member creator, List<String> options, String description,
|
||||
Boolean allowMultiple, Boolean allowAddition, Boolean showDecisions, Duration pollDuration) {
|
||||
Long serverId = creator.getGuild().getIdLong();
|
||||
HashSet<String> optionAsSet = new HashSet<>(options);
|
||||
if(optionAsSet.size() != options.size()) {
|
||||
throw new PollOptionAlreadyExistsException();
|
||||
}
|
||||
Long pollId = counterService.getNextCounterValue(serverId, POLLS_COUNTER_KEY);
|
||||
log.info("Creating server poll {} in server {} because of user {}.", pollId, serverId, creator.getIdLong());
|
||||
List<PollMessageOption> parsedOptions = parseOptions(options);
|
||||
String selectionMenuId = componentService.generateComponentId();
|
||||
String addOptionButtonId = componentService.generateComponentId();
|
||||
if(pollDuration == null) {
|
||||
Long pollDurationSeconds = configService.getLongValueOrConfigDefault(PollService.SERVER_POLL_DURATION_SECONDS, serverId);
|
||||
log.info("No duration provided - using {} seconds from configuration.", pollDurationSeconds);
|
||||
pollDuration = Duration.ofSeconds(pollDurationSeconds);
|
||||
}
|
||||
Instant targetDate = Instant.now().plus(pollDuration);
|
||||
HashMap<Object, Object> parameters = new HashMap<>();
|
||||
parameters.put("serverId", serverId.toString());
|
||||
parameters.put("pollId", pollId.toString());
|
||||
JobParameters jobParameters = JobParameters.builder().parameters(parameters).build();
|
||||
String triggerKey = null;
|
||||
if(featureModeService.featureModeActive(SuggestionFeatureDefinition.POLL, serverId, PollFeatureMode.POLL_AUTO_EVALUATE)) {
|
||||
log.info("Creating scheduled job to evaluate poll {} in server {} at {}.", pollId, serverId, targetDate);
|
||||
triggerKey = schedulerService.executeJobWithParametersOnce("serverPollEvaluationJob", "poll", jobParameters, Date.from(targetDate));
|
||||
}
|
||||
String reminderTriggerKey = null;
|
||||
if(featureModeService.featureModeActive(SuggestionFeatureDefinition.POLL, serverId, PollFeatureMode.POLL_REMINDER)) {
|
||||
log.info("Creating scheduled job to remind about poll {} in server {} at {}.", pollId, serverId, targetDate);
|
||||
reminderTriggerKey = schedulerService.executeJobWithParametersOnce("serverPollReminderJob", "poll", jobParameters, Date.from(targetDate));
|
||||
}
|
||||
PollCreationRequest pollCreationRequest = PollCreationRequest
|
||||
.builder()
|
||||
.pollId(pollId)
|
||||
.type(PollType.STANDARD)
|
||||
.allowAddition(allowAddition)
|
||||
.allowMultiple(allowMultiple)
|
||||
.showDecisions(showDecisions)
|
||||
.addOptionButtonId(addOptionButtonId)
|
||||
.reminderJobTrigger(reminderTriggerKey)
|
||||
.selectionMenuId(selectionMenuId)
|
||||
.serverId(serverId)
|
||||
.evaluationJobTrigger(triggerKey)
|
||||
.targetDate(targetDate)
|
||||
.creatorId(creator.getIdLong())
|
||||
.description(description)
|
||||
.options(parsedOptions)
|
||||
.build();
|
||||
|
||||
ServerPollMessageModel model = ServerPollMessageModel
|
||||
.builder()
|
||||
.creator(MemberDisplay.fromMember(creator))
|
||||
.description(description)
|
||||
.pollId(pollId)
|
||||
.state(PollState.NEW)
|
||||
.allowMultiple(allowMultiple)
|
||||
.showDecisions(showDecisions)
|
||||
.allowAdditions(allowAddition)
|
||||
.endDate(targetDate)
|
||||
.options(parsedOptions)
|
||||
.addOptionButtonId(addOptionButtonId)
|
||||
.selectionMenuId(selectionMenuId)
|
||||
.build();
|
||||
ServerPollSelectionMenuPayload payload = ServerPollSelectionMenuPayload
|
||||
.builder()
|
||||
.serverId(serverId)
|
||||
.pollId(pollId)
|
||||
.build();
|
||||
SelectMenuConfigModel selectMenuConfigModel = SelectMenuConfigModel
|
||||
.builder()
|
||||
.selectMenuId(selectionMenuId)
|
||||
.origin(SERVER_POLL_SELECTION_MENU_ORIGIN)
|
||||
.selectMenuPayload(payload)
|
||||
.payloadType(ServerPollSelectionMenuPayload.class)
|
||||
.build();
|
||||
componentPayloadManagementService.createStringSelectMenuPayload(selectMenuConfigModel, serverId);
|
||||
PollAddOptionButtonPayload buttonPayload = PollAddOptionButtonPayload
|
||||
.builder()
|
||||
.serverId(serverId)
|
||||
.pollId(pollId)
|
||||
.build();
|
||||
ButtonConfigModel buttonConfigModel = ButtonConfigModel
|
||||
.builder()
|
||||
.buttonId(addOptionButtonId)
|
||||
.buttonPayload(buttonPayload)
|
||||
.origin(SERVER_POLL_ADD_OPTION_ORIGIN)
|
||||
.payloadType(PollAddOptionButtonPayload.class)
|
||||
.build();
|
||||
componentPayloadManagementService.createButtonPayload(buttonConfigModel, serverId);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SERVER_POLL_TEMPLATE_KEY, model);
|
||||
List<CompletableFuture<Message>> messageFutures = postTargetService.sendEmbedInPostTarget(messageToSend, PollPostTarget.POLLS, serverId);
|
||||
return FutureUtils.toSingleFutureGeneric(messageFutures)
|
||||
.thenAccept(unused -> self.persistPoll(messageFutures.get(0).join(), pollCreationRequest));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> createQuickPoll(Member creator, List<String> options, String description,
|
||||
Boolean allowMultiple, Boolean showDecisions, InteractionHook interactionHook, Duration pollDuration) {
|
||||
HashSet<String> optionAsSet = new HashSet<>(options);
|
||||
if(optionAsSet.size() != options.size()) {
|
||||
throw new PollOptionAlreadyExistsException();
|
||||
}
|
||||
Long serverId = creator.getGuild().getIdLong();
|
||||
Long pollId = counterService.getNextCounterValue(serverId, QUICK_POLLS_COUNTER_KEY);
|
||||
log.info("Creating quick poll {} in server {} because of user {}.", pollId, serverId, creator.getIdLong());
|
||||
List<PollMessageOption> parsedOptions = parseOptions(options);
|
||||
String selectionMenuId = componentService.generateComponentId();
|
||||
if(pollDuration == null) {
|
||||
Long pollDurationSeconds = configService.getLongValueOrConfigDefault(PollService.QUICK_POLL_DURATION_SECONDS, serverId);
|
||||
log.info("No duration provided - using {} seconds from configuration.", pollDurationSeconds);
|
||||
pollDuration = Duration.ofSeconds(pollDurationSeconds);
|
||||
}
|
||||
Instant targetDate = Instant.now().plus(pollDuration);
|
||||
HashMap<Object, Object> parameters = new HashMap<>();
|
||||
parameters.put("serverId", serverId.toString());
|
||||
parameters.put("pollId", pollId.toString());
|
||||
JobParameters jobParameters = JobParameters.builder().parameters(parameters).build();
|
||||
String triggerKey = schedulerService.executeJobWithParametersOnce("quickPollEvaluationJob", "poll", jobParameters, Date.from(targetDate));
|
||||
log.info("Starting scheduled job to evaluate quick poll.");
|
||||
PollCreationRequest pollCreationRequest = PollCreationRequest
|
||||
.builder()
|
||||
.pollId(pollId)
|
||||
.type(PollType.QUICK)
|
||||
.allowMultiple(allowMultiple)
|
||||
.evaluationJobTrigger(triggerKey)
|
||||
.showDecisions(showDecisions)
|
||||
.selectionMenuId(selectionMenuId)
|
||||
.serverId(serverId)
|
||||
.allowAddition(false)
|
||||
.targetDate(targetDate)
|
||||
.creatorId(creator.getIdLong())
|
||||
.description(description)
|
||||
.options(parsedOptions)
|
||||
.build();
|
||||
|
||||
QuickPollMessageModel model = QuickPollMessageModel
|
||||
.builder()
|
||||
.creator(MemberDisplay.fromMember(creator))
|
||||
.description(description)
|
||||
.pollId(pollId)
|
||||
.allowMultiple(allowMultiple)
|
||||
.showDecisions(showDecisions)
|
||||
.endDate(targetDate)
|
||||
.options(parsedOptions)
|
||||
.selectionMenuId(selectionMenuId)
|
||||
.build();
|
||||
QuickPollSelectionMenuPayload payload = QuickPollSelectionMenuPayload
|
||||
.builder()
|
||||
.serverId(serverId)
|
||||
.pollId(pollId)
|
||||
.build();
|
||||
SelectMenuConfigModel selectMenuConfigModel = SelectMenuConfigModel
|
||||
.builder()
|
||||
.selectMenuId(selectionMenuId)
|
||||
.origin(QUICK_POLL_SELECTION_MENU_ORIGIN)
|
||||
.selectMenuPayload(payload)
|
||||
.payloadType(QuickPollSelectionMenuPayload.class)
|
||||
.build();
|
||||
componentPayloadManagementService.createStringSelectMenuPayload(selectMenuConfigModel, serverId);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(QUICK_POLL_TEMPLATE_KEY, model);
|
||||
List<CompletableFuture<Message>> messageFutures = interactionService.sendMessageToInteraction(messageToSend, interactionHook);
|
||||
return FutureUtils.toSingleFutureGeneric(messageFutures)
|
||||
.thenAccept(unused -> self.persistPoll(messageFutures.get(0).join(), pollCreationRequest));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> setDecisionsInPollTo(Member voter, List<String> chosenValues, Long pollId, PollType pollType) {
|
||||
Poll poll = pollManagementService.getPollByPollId(pollId, voter.getGuild().getIdLong(), pollType);
|
||||
log.info("Adding decisions of user {} to poll {}.", voter.getIdLong(), poll.getPollId());
|
||||
AUserInAServer userInServer = userInServerManagementService.loadOrCreateUser(voter);
|
||||
Optional<PollUserDecision> decisionOptional = pollUserDecisionManagementService.getUserDecisionOptional(poll, userInServer);
|
||||
PollUserDecision decision;
|
||||
boolean needToSave = false;
|
||||
if(decisionOptional.isPresent()) {
|
||||
decision = decisionOptional.get();
|
||||
} else {
|
||||
needToSave = true;
|
||||
decision = pollUserDecisionManagementService.createUserDecision(poll, userInServer);
|
||||
}
|
||||
Long optionsAdded = 0L;
|
||||
for (PollOption pollOption : poll.getOptions()) {
|
||||
if (chosenValues.contains(pollOption.getValue()) &&
|
||||
(decision.getOptions() == null || decision.getOptions().stream().noneMatch(pollUserDecisionOption -> pollUserDecisionOption.getPollOption().getLabel().equals(pollOption.getValue())))) {
|
||||
pollUserDecisionOptionManagementService.addDecisionForUser(decision, pollOption);
|
||||
optionsAdded += 1;
|
||||
}
|
||||
}
|
||||
log.info("Added {} options to poll {} for user {}.", optionsAdded, pollId, voter.getIdLong());
|
||||
|
||||
if(decision.getOptions() != null) {
|
||||
List<PollUserDecisionOption> toRemove = decision
|
||||
.getOptions()
|
||||
.stream()
|
||||
.filter(pollUserDecisionOption -> !chosenValues.contains(pollUserDecisionOption.getPollOption().getLabel()))
|
||||
.collect(Collectors.toList());
|
||||
log.info("Removing {} options from poll {} for user {}.", toRemove.size(), pollId, voter.getIdLong());
|
||||
pollUserDecisionOptionManagementService.deleteDecisionOptions(decision, toRemove);
|
||||
}
|
||||
if(needToSave) {
|
||||
pollUserDecisionManagementService.savePollUserDecision(decision);
|
||||
}
|
||||
if(poll.getShowDecisions()) {
|
||||
return updatePollMessage(poll, voter.getGuild());
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> addOptionToServerPoll(Long pollId, Long serverId, Member adder, String label, String description) {
|
||||
Poll poll = pollManagementService.getPollByPollId(pollId, serverId, PollType.STANDARD);
|
||||
log.info("Adding option to server poll {} in server {}.", pollId, serverId);
|
||||
pollOptionManagementService.addOptionToPoll(poll, label, description);
|
||||
List<PollMessageOption> options = getOptionsOfPoll(poll);
|
||||
ServerPollMessageModel model = ServerPollMessageModel.fromPoll(poll, options);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SERVER_POLL_TEMPLATE_KEY, model);
|
||||
MessageChannel pollChannel = adder.getGuild().getChannelById(MessageChannel.class, poll.getChannel().getId());
|
||||
List<CompletableFuture<Message>> messageFutures = channelService.editMessagesInAChannelFuture(messageToSend, pollChannel, Arrays.asList(poll.getMessageId()));
|
||||
return FutureUtils.toSingleFutureGeneric(messageFutures);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CompletableFuture<Void> evaluateServerPoll(Long pollId, Long serverId) {
|
||||
Poll poll = pollManagementService.getPollByPollId(pollId, serverId, PollType.STANDARD);
|
||||
log.info("Evaluating server poll {} in server {}.", pollId, serverId);
|
||||
poll.setState(PollState.FINISHED);
|
||||
List<PollMessageOption> allOptions = getOptionsOfPoll(poll);
|
||||
List<PollMessageOption> topOptions = allOptions;
|
||||
if(!allOptions.isEmpty()) {
|
||||
Integer mostVotes = allOptions
|
||||
.stream()
|
||||
.sorted(Comparator.comparingInt(PollMessageOption::getVotes).reversed())
|
||||
.collect(Collectors.toList()).get(0).getVotes();
|
||||
topOptions = allOptions
|
||||
.stream()
|
||||
.filter(pollMessageOption -> pollMessageOption.getVotes().equals(mostVotes))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
ServerPollEvaluationModel model = ServerPollEvaluationModel
|
||||
.builder()
|
||||
.pollId(pollId)
|
||||
.options(allOptions)
|
||||
.pollMessageId(poll.getMessageId())
|
||||
.topOptions(topOptions)
|
||||
.description(poll.getDescription())
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SERVER_POLL_EVALUATION_UPDATE_TEMPLATE_KEY, model);
|
||||
log.info("Sending update message for poll evaluation of server poll {} in server {}.", pollId, serverId);
|
||||
List<CompletableFuture<Message>> messageFutures = postTargetService.sendEmbedInPostTarget(messageToSend, PollPostTarget.POLLS, serverId);
|
||||
GuildMessageChannel channel = channelService.getMessageChannelFromServer(serverId, poll.getChannel().getId());
|
||||
log.info("Cleaning existing components in message {} for server poll {} in server {}.", poll.getMessageId(), pollId, serverId);
|
||||
CompletableFuture<Message> cleanMessageFuture = channelService.removeComponents(channel, poll.getMessageId());
|
||||
return CompletableFuture.allOf(FutureUtils.toSingleFutureGeneric(messageFutures), cleanMessageFuture)
|
||||
.thenAccept(unused -> self.updateFinalPollMessage(pollId, channel.getGuild()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CompletableFuture<Void> remindServerPoll(Long pollId, Long serverId) {
|
||||
Poll poll = pollManagementService.getPollByPollId(pollId, serverId, PollType.STANDARD);
|
||||
log.info("Reminding about server poll {} in server {}.", pollId, serverId);
|
||||
List<PollMessageOption> allOptions = getOptionsOfPoll(poll);
|
||||
List<PollMessageOption> topOptions = allOptions;
|
||||
if(!allOptions.isEmpty()) {
|
||||
Integer mostVotes = allOptions
|
||||
.stream()
|
||||
.sorted(Comparator.comparingInt(PollMessageOption::getVotes).reversed())
|
||||
.collect(Collectors.toList()).get(0).getVotes();
|
||||
topOptions = allOptions
|
||||
.stream()
|
||||
.filter(pollMessageOption -> pollMessageOption.getVotes().equals(mostVotes))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
ServerPollReminderModel model = ServerPollReminderModel
|
||||
.builder()
|
||||
.pollId(pollId)
|
||||
.options(allOptions)
|
||||
.topOptions(topOptions)
|
||||
.messageLink(MessageUtils.buildMessageUrl(serverId, poll.getChannel().getId(), poll.getMessageId()))
|
||||
.description(poll.getDescription())
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SERVER_POLL_REMINDER_TEMPLATE_KEY, model);
|
||||
log.info("Sending poll reminder about server poll {} in server {}.", pollId, serverId);
|
||||
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, PollPostTarget.POLL_REMINDER, serverId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CompletableFuture<Void> evaluateQuickPoll(Long pollId, Long serverId) {
|
||||
Poll poll = pollManagementService.getPollByPollId(pollId, serverId, PollType.QUICK);
|
||||
log.info("Evaluating quick poll {} in server {}.", pollId, serverId);
|
||||
poll.setState(PollState.FINISHED);
|
||||
List<PollMessageOption> allOptions = getOptionsOfPoll(poll);
|
||||
List<PollMessageOption> topOptions = allOptions;
|
||||
if(!allOptions.isEmpty()) {
|
||||
Integer mostVotes = allOptions
|
||||
.stream()
|
||||
.sorted(Comparator.comparingInt(PollMessageOption::getVotes).reversed())
|
||||
.collect(Collectors.toList()).get(0).getVotes();
|
||||
topOptions = allOptions
|
||||
.stream()
|
||||
.filter(pollMessageOption -> pollMessageOption.getVotes().equals(mostVotes))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
QuickPollEvaluationModel model = QuickPollEvaluationModel
|
||||
.builder()
|
||||
.pollId(pollId)
|
||||
.options(allOptions)
|
||||
.pollMessageId(poll.getMessageId())
|
||||
.topOptions(topOptions)
|
||||
.description(poll.getDescription())
|
||||
.build();
|
||||
MessageChannel channel = channelService.getMessageChannelFromServer(serverId, poll.getChannel().getId());
|
||||
CompletableFuture<Message> removeComponentFuture = channelService.removeComponents(channel, poll.getMessageId());
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(QUICK_POLL_EVALUATION_UPDATE_TEMPLATE_KEY, model);
|
||||
CompletableFuture<Void> updateMessageFuture = FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, channel));
|
||||
return CompletableFuture.allOf(removeComponentFuture, updateMessageFuture)
|
||||
.thenApply(message -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> closePoll(Long pollId, Long serverId, String text, Member cause) {
|
||||
Poll poll = pollManagementService.getPollByPollId(pollId, serverId, PollType.STANDARD);
|
||||
log.info("Member {} closes poll {} in server {}.", cause.getIdLong(), pollId, serverId);
|
||||
PollClosingMessageModel model = PollClosingMessageModel
|
||||
.builder()
|
||||
.pollMessageId(poll.getMessageId())
|
||||
.cause(MemberNameDisplay.fromMember(cause))
|
||||
.pollId(pollId)
|
||||
.text(text)
|
||||
.serverId(serverId)
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SERVER_POLL_CLOSE_MESSAGE, model);
|
||||
List<CompletableFuture<Message>> messageFutures = postTargetService.sendEmbedInPostTarget(messageToSend, PollPostTarget.POLLS, serverId);
|
||||
MessageChannel channel = channelService.getMessageChannelFromServer(serverId, poll.getChannel().getId());
|
||||
CompletableFuture<Message> removeComponentsFuture = channelService.removeComponents(channel, poll.getMessageId());
|
||||
return CompletableFuture.allOf(FutureUtils.toSingleFutureGeneric(messageFutures), removeComponentsFuture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> cancelPoll(Long pollId, Long serverId, Member cause) {
|
||||
Poll poll = pollManagementService.getPollByPollId(pollId, serverId, PollType.STANDARD);
|
||||
log.info("Member {} cancelled poll {} in server {}.", cause.getIdLong(), pollId, serverId);
|
||||
if(!poll.getCreator().getUserReference().getId().equals(cause.getIdLong()) ||
|
||||
poll.getCreated().isBefore(Instant.now().minus(Duration.ofSeconds(removalMaxAgeSeconds)))) {
|
||||
throw new PollCancellationNotPossibleException();
|
||||
}
|
||||
if(poll.getReminderJobTriggerKey() != null) {
|
||||
schedulerService.stopTrigger(poll.getReminderJobTriggerKey());
|
||||
}
|
||||
if(poll.getEvaluationJobTriggerKey() != null) {
|
||||
schedulerService.stopTrigger(poll.getEvaluationJobTriggerKey());
|
||||
}
|
||||
poll.setState(PollState.CANCELLED);
|
||||
return messageService.deleteMessageInChannelInServer(serverId, poll.getChannel().getId(), poll.getMessageId());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Void> updateFinalPollMessage(Long pollId, Guild guild) {
|
||||
Poll poll = pollManagementService.getPollByPollId(pollId, guild.getIdLong(), PollType.STANDARD);
|
||||
List<PollMessageOption> options = getOptionsOfPoll(poll);
|
||||
ServerPollMessageModel model = ServerPollMessageModel.fromPoll(poll, options);
|
||||
model.setAllowAdditions(false);
|
||||
model.setShowDecisions(true);
|
||||
model.setAllowMultiple(false);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SERVER_POLL_TEMPLATE_KEY, model);
|
||||
MessageChannel pollChannel = guild.getChannelById(MessageChannel.class, poll.getChannel().getId());
|
||||
return channelService.editEmbedMessageInAChannel(messageToSend.getEmbeds().get(0), pollChannel, poll.getMessageId())
|
||||
.thenApply(message -> null);
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> updatePollMessage(Poll poll, Guild guild) {
|
||||
List<PollMessageOption> options = getOptionsOfPoll(poll);
|
||||
ServerPollMessageModel model = ServerPollMessageModel.fromPoll(poll, options);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SERVER_POLL_TEMPLATE_KEY, model);
|
||||
MessageChannel pollChannel = guild.getChannelById(MessageChannel.class, poll.getChannel().getId());
|
||||
return channelService.editEmbedMessageInAChannel(messageToSend.getEmbeds().get(0), pollChannel, poll.getMessageId())
|
||||
.thenApply(message -> null);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void persistPoll(Message message, PollCreationRequest pollCreationRequest) {
|
||||
if(message == null) {
|
||||
log.info("Post target was not setup - no message created.");
|
||||
return;
|
||||
}
|
||||
pollCreationRequest.setPollMessageId(message.getIdLong());
|
||||
pollCreationRequest.setPollChannelId(message.getChannel().getIdLong());
|
||||
log.info("Persisting poll {} shown in message {} in channel {} in server {}.",
|
||||
pollCreationRequest.getPollId(), pollCreationRequest.getPollMessageId(), pollCreationRequest.getPollChannelId(),
|
||||
pollCreationRequest.getServerId());
|
||||
Poll createdPoll = pollManagementService.createPoll(pollCreationRequest);
|
||||
log.info("Adding {} options to poll {}.", pollCreationRequest.getOptions().size(), pollCreationRequest.getPollId());
|
||||
pollOptionManagementService.addOptionsToPoll(createdPoll, pollCreationRequest);
|
||||
}
|
||||
|
||||
private List<PollMessageOption> parseOptions(List<String> options) {
|
||||
return options.stream().map(s -> {
|
||||
String label = s;
|
||||
String description = "";
|
||||
if(s.contains(";")) {
|
||||
String[] splitOption = s.split(";");
|
||||
label = splitOption[0];
|
||||
description = splitOption[1];
|
||||
}
|
||||
return PollMessageOption
|
||||
.builder()
|
||||
.label(label)
|
||||
.value(label)
|
||||
.votes(0)
|
||||
.percentage(0f)
|
||||
.description(description)
|
||||
.build();
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<PollMessageOption> getOptionsOfPoll(Poll poll) {
|
||||
Integer totalVotes = poll
|
||||
.getDecisions()
|
||||
.stream()
|
||||
.map(userDecision -> userDecision.getOptions().size())
|
||||
.mapToInt(Integer::intValue)
|
||||
.sum();
|
||||
return poll.getOptions().stream().map(option -> {
|
||||
Long voteCount = poll
|
||||
.getDecisions()
|
||||
.stream()
|
||||
.filter(decision -> decision.getOptions().stream().anyMatch(pollUserDecisionOption -> pollUserDecisionOption.getPollOption().equals(option)))
|
||||
.count();
|
||||
return PollMessageOption
|
||||
.builder()
|
||||
.value(option.getValue())
|
||||
.label(option.getLabel())
|
||||
.votes(voteCount.intValue())
|
||||
.percentage(totalVotes > 0 ? (voteCount / (float) totalVotes) * 100 : 0)
|
||||
.description(option.getDescription())
|
||||
.build();
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package dev.sheldan.abstracto.suggestion.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.suggestion.exception.PollNotFoundException;
|
||||
import dev.sheldan.abstracto.suggestion.model.PollCreationRequest;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.Poll;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollState;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollType;
|
||||
import dev.sheldan.abstracto.suggestion.repository.PollRepository;
|
||||
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 PollManagementServiceBean implements PollManagementService {
|
||||
|
||||
@Autowired
|
||||
private PollRepository pollRepository;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private ChannelManagementService channelManagementService;
|
||||
|
||||
@Override
|
||||
public Poll createPoll(PollCreationRequest pollCreationRequest) {
|
||||
ServerUser creatorServerUser = ServerUser
|
||||
.builder()
|
||||
.userId(pollCreationRequest.getCreatorId())
|
||||
.serverId(pollCreationRequest.getServerId())
|
||||
.build();
|
||||
AUserInAServer creator = userInServerManagementService.loadOrCreateUser(creatorServerUser);
|
||||
AChannel channel = channelManagementService.loadChannel(pollCreationRequest.getPollChannelId());
|
||||
Poll pollInstance = Poll
|
||||
.builder()
|
||||
.description(pollCreationRequest.getDescription())
|
||||
.server(creator.getServerReference())
|
||||
.pollId(pollCreationRequest.getPollId())
|
||||
.allowMultiple(pollCreationRequest.getAllowMultiple())
|
||||
.allowAddition(pollCreationRequest.getAllowAddition())
|
||||
.showDecisions(pollCreationRequest.getShowDecisions())
|
||||
.reminderJobTriggerKey(pollCreationRequest.getReminderJobTrigger())
|
||||
.targetDate(pollCreationRequest.getTargetDate())
|
||||
.evaluationJobTriggerKey(pollCreationRequest.getEvaluationJobTrigger())
|
||||
.messageId(pollCreationRequest.getPollMessageId())
|
||||
.channel(channel)
|
||||
.addOptionButtonId(pollCreationRequest.getAddOptionButtonId())
|
||||
.selectionMenuId(pollCreationRequest.getSelectionMenuId())
|
||||
.creator(creator)
|
||||
.state(PollState.NEW)
|
||||
.type(pollCreationRequest.getType())
|
||||
.build();
|
||||
return pollRepository.save(pollInstance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Poll getPollByPollId(Long pollId, Long serverId, PollType pollType) {
|
||||
return getPollByPollIdOptional(pollId, serverId, pollType).orElseThrow(() -> new PollNotFoundException(pollId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Poll> getPollByPollIdOptional(Long pollId, Long serverId, PollType pollType) {
|
||||
return pollRepository.findByPollIdAndServer_IdAndType(pollId, serverId, pollType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package dev.sheldan.abstracto.suggestion.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.suggestion.model.PollCreationRequest;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.Poll;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollOption;
|
||||
import dev.sheldan.abstracto.suggestion.repository.PollOptionRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class PollOptionManagementServiceBean implements PollOptionManagementService {
|
||||
|
||||
@Autowired
|
||||
private PollOptionRepository pollOptionRepository;
|
||||
|
||||
@Override
|
||||
public void addOptionsToPoll(Poll poll, PollCreationRequest pollCreationRequest) {
|
||||
List<PollOption> options = pollCreationRequest.getOptions().stream().map(option -> PollOption
|
||||
.builder()
|
||||
.poll(poll)
|
||||
.server(poll.getServer())
|
||||
.label(option.getLabel())
|
||||
.value(option.getLabel())
|
||||
.description(option.getDescription())
|
||||
.build()).collect(Collectors.toList());
|
||||
pollOptionRepository.saveAll(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOptionToPoll(Poll poll, String label, String description) {
|
||||
PollOption option = PollOption
|
||||
.builder()
|
||||
.poll(poll)
|
||||
.label(label)
|
||||
.value(label)
|
||||
.server(poll.getServer())
|
||||
.description(description)
|
||||
.build();
|
||||
pollOptionRepository.save(option);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<PollOption> getPollOptionByName(Poll poll, String key) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package dev.sheldan.abstracto.suggestion.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.Poll;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollUserDecision;
|
||||
import dev.sheldan.abstracto.suggestion.repository.PollUserDecisionRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class PollUserDecisionManagementServiceBean implements PollUserDecisionManagementService {
|
||||
|
||||
@Autowired
|
||||
private PollUserDecisionRepository repository;
|
||||
|
||||
@Override
|
||||
public PollUserDecision addUserDecision(Poll poll, AUserInAServer user) {
|
||||
return repository.save(createUserDecision(poll, user));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PollUserDecision createUserDecision(Poll poll, AUserInAServer user) {
|
||||
return PollUserDecision
|
||||
.builder()
|
||||
.server(user.getServerReference())
|
||||
.voter(user)
|
||||
.options(new ArrayList<>())
|
||||
.poll(poll)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<PollUserDecision> getUserDecisionOptional(Poll poll, AUserInAServer user) {
|
||||
return repository.findPollUserDecisionByPollAndVoter(poll, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PollUserDecision getUserDecision(Poll poll, AUserInAServer user) {
|
||||
return repository.findPollUserDecisionByPollAndVoter(poll, user).orElseThrow(() -> new AbstractoRunTimeException("User decision not found."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void savePollUserDecision(PollUserDecision pollUserDecision) {
|
||||
repository.save(pollUserDecision);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package dev.sheldan.abstracto.suggestion.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollOption;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollUserDecision;
|
||||
import dev.sheldan.abstracto.suggestion.model.database.PollUserDecisionOption;
|
||||
import dev.sheldan.abstracto.suggestion.repository.PollUserDecisionOptionRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class PollUserDecisionOptionManagementServiceBean implements PollUserDecisionOptionManagementService {
|
||||
|
||||
@Autowired
|
||||
private PollUserDecisionOptionRepository repository;
|
||||
|
||||
@Override
|
||||
public PollUserDecisionOption addDecisionForUser(PollUserDecision decision, PollOption pollOption) {
|
||||
PollUserDecisionOption option = PollUserDecisionOption
|
||||
.builder()
|
||||
.decision(decision)
|
||||
.poll(decision.getPoll())
|
||||
.pollOption(pollOption)
|
||||
.build();
|
||||
decision.getOptions().add(option);
|
||||
return option;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearOptions(PollUserDecision pollUserDecision) {
|
||||
repository.deleteAll(pollUserDecision.getOptions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteDecisionOptions(PollUserDecision decision, List<PollUserDecisionOption> decisionOptionList) {
|
||||
decision.getOptions().removeAll(decisionOptionList);
|
||||
repository.deleteAll(decisionOptionList);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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="seedData/data.xml" relativeToChangelogFile="true"/>
|
||||
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?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="pollFeature" value="(SELECT id FROM feature WHERE key = 'poll')"/>
|
||||
|
||||
<changeSet author="Sheldan" id="poll-commands">
|
||||
<insert tableName="command">
|
||||
<column name="name" value="poll"/>
|
||||
<column name="module_id" valueComputed="${utilityModule}"/>
|
||||
<column name="feature_id" valueComputed="${pollFeature}"/>
|
||||
</insert>
|
||||
<insert tableName="command">
|
||||
<column name="name" value="quickPoll"/>
|
||||
<column name="module_id" valueComputed="${utilityModule}"/>
|
||||
<column name="feature_id" valueComputed="${pollFeature}"/>
|
||||
</insert>
|
||||
<insert tableName="command">
|
||||
<column name="name" value="closePoll"/>
|
||||
<column name="module_id" valueComputed="${utilityModule}"/>
|
||||
<column name="feature_id" valueComputed="${pollFeature}"/>
|
||||
</insert>
|
||||
<insert tableName="command">
|
||||
<column name="name" value="cancelPoll"/>
|
||||
<column name="module_id" valueComputed="${utilityModule}"/>
|
||||
<column name="feature_id" valueComputed="${pollFeature}"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -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="poll_jobs.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -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="poll_feature-insertion">
|
||||
<insert tableName="feature">
|
||||
<column name="key" value="poll"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?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="poll_jobs-insert">
|
||||
<insert tableName="scheduler_job">
|
||||
<column name="name" value="serverPollEvaluationJob"/>
|
||||
<column name="group_name" value="poll"/>
|
||||
<column name="clazz" value="dev.sheldan.abstracto.suggestion.job.ServerPollEvaluationJob"/>
|
||||
<column name="active" value="true"/>
|
||||
<column name="recovery" value="false"/>
|
||||
</insert>
|
||||
<insert tableName="scheduler_job">
|
||||
<column name="name" value="quickPollEvaluationJob"/>
|
||||
<column name="group_name" value="poll"/>
|
||||
<column name="clazz" value="dev.sheldan.abstracto.suggestion.job.QuickPollEvaluationJob"/>
|
||||
<column name="active" value="true"/>
|
||||
<column name="recovery" value="false"/>
|
||||
</insert>
|
||||
<insert tableName="scheduler_job">
|
||||
<column name="name" value="serverPollReminderJob"/>
|
||||
<column name="group_name" value="poll"/>
|
||||
<column name="clazz" value="dev.sheldan.abstracto.suggestion.job.ServerPollReminderJob"/>
|
||||
<column name="active" value="true"/>
|
||||
<column name="recovery" value="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,89 @@
|
||||
<?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="poll-table">
|
||||
<createTable tableName="poll">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints nullable="false" primaryKey="true"/>
|
||||
</column>
|
||||
<column name="poll_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="message_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="type" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
|
||||
<column name="channel_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="state" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="selection_menu_id" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="add_option_button_id" type="VARCHAR(100)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="creator_user_in_server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="description" type="VARCHAR(2000)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="evaluation_job_trigger_key" type="varchar(255)"/>
|
||||
<column name="reminder_job_trigger_key" type="varchar(255)"/>
|
||||
<column name="target_date" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="allow_multiple" type="BOOLEAN">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="show_decisions" type="BOOLEAN">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="allow_addition" type="BOOLEAN">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addUniqueConstraint
|
||||
columnNames="poll_id, server_id, type"
|
||||
constraintName="uq_poll_id"
|
||||
tableName="poll"
|
||||
/>
|
||||
<addForeignKeyConstraint baseColumnNames="channel_id" baseTableName="poll" constraintName="fk_poll_channel"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="channel" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="creator_user_in_server_id" baseTableName="poll" constraintName="fk_poll_creator"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="poll" constraintName="fk_poll_server"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="server" validate="true"/>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS poll_update_trigger ON poll;
|
||||
CREATE TRIGGER poll_update_trigger BEFORE UPDATE ON poll FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS poll_insert_trigger ON poll;
|
||||
CREATE TRIGGER poll_insert_trigger BEFORE INSERT ON poll FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
ALTER TABLE poll ADD CONSTRAINT check_poll_state CHECK (state IN ('NEW', 'FINISHED','CANCELLED', 'VETOED'));
|
||||
</sql>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?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="poll_option-table">
|
||||
<createTable tableName="poll_option">
|
||||
<column name="id" autoIncrement="true" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="poll_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"/>
|
||||
<column name="server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="label" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="value" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="description" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="adder_user_in_server_id" type="BIGINT">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addPrimaryKey columnNames="id" tableName="poll_option" constraintName="pk_poll_option" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="adder_user_in_server_id" baseTableName="poll_option" constraintName="fk_poll_option_adder"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="poll_option" constraintName="fk_poll_option_server"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="poll_id" baseTableName="poll_option" constraintName="fk_poll_option_poll"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="poll" validate="true"/>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS poll_option_update_trigger ON poll_option;
|
||||
CREATE TRIGGER poll_option_update_trigger BEFORE UPDATE ON poll_option FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS poll_option_insert_trigger ON poll_option;
|
||||
CREATE TRIGGER poll_option_insert_trigger BEFORE INSERT ON poll_option FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||
</sql>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?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="poll_user_decision-table">
|
||||
<createTable tableName="poll_user_decision">
|
||||
<column name="id" autoIncrement="true" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true"/>
|
||||
</column>
|
||||
<column name="user_in_server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="poll_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"/>
|
||||
<column name="server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addUniqueConstraint
|
||||
columnNames="user_in_server_id, poll_id"
|
||||
constraintName="uq_poll_user_decision"
|
||||
tableName="poll_user_decision"
|
||||
/>
|
||||
<addForeignKeyConstraint baseColumnNames="user_in_server_id" baseTableName="poll_user_decision" constraintName="fk_poll_user_decision_user"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="poll_user_decision" constraintName="fk_poll_user_decision_server"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="poll_id" baseTableName="poll_user_decision" constraintName="fk_poll_user_decision_poll"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="poll" validate="true"/>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS poll_user_decision_update_trigger ON poll_user_decision;
|
||||
CREATE TRIGGER poll_user_decision_update_trigger BEFORE UPDATE ON poll_user_decision FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS poll_user_decision_insert_trigger ON poll_user_decision;
|
||||
CREATE TRIGGER poll_user_decision_insert_trigger BEFORE INSERT ON poll_user_decision FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||
</sql>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?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="poll_user_decision_option-table">
|
||||
<createTable tableName="poll_user_decision_option">
|
||||
<column name="id" autoIncrement="true" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true"/>
|
||||
</column>
|
||||
<column name="user_decision_id" type="BIGINT">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
<column name="poll_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="option_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>
|
||||
<addUniqueConstraint
|
||||
columnNames="user_decision_id, poll_id, option_id"
|
||||
constraintName="uq_poll_user_decision_option"
|
||||
tableName="poll_user_decision_option"
|
||||
/>
|
||||
<addForeignKeyConstraint baseColumnNames="user_decision_id" baseTableName="poll_user_decision_option" constraintName="fk_poll_user_decision_option_decision"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="poll_user_decision" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="option_id" baseTableName="poll_user_decision_option" constraintName="fk_poll_user_decision_option_option"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="poll_option" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="poll_id" baseTableName="poll_user_decision_option" constraintName="fk_poll_user_decision_option_poll"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="poll" validate="true"/>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS poll_user_decision_option_update_trigger ON poll_user_decision_option;
|
||||
CREATE TRIGGER poll_user_decision_option_trigger BEFORE UPDATE ON poll_user_decision_option FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS poll_user_decision_option_insert_trigger ON poll_user_decision_option;
|
||||
CREATE TRIGGER poll_user_decision_option_insert_trigger BEFORE INSERT ON poll_user_decision_option FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||
</sql>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,13 @@
|
||||
<?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="poll.xml" relativeToChangelogFile="true"/>
|
||||
<include file="poll_option.xml" relativeToChangelogFile="true"/>
|
||||
<include file="poll_user_decision.xml" relativeToChangelogFile="true"/>
|
||||
<include file="poll_user_decision_option.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -12,4 +12,5 @@
|
||||
<include file="1.3.8/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.4.0/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.4.8/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.4.26/collection.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -30,4 +30,26 @@ abstracto.featureModes.suggestionAutoEvaluate.enabled=false
|
||||
|
||||
abstracto.featureModes.suggestionButton.featureName=suggestion
|
||||
abstracto.featureModes.suggestionButton.mode=suggestionButton
|
||||
abstracto.featureModes.suggestionButton.enabled=true
|
||||
abstracto.featureModes.suggestionButton.enabled=true
|
||||
|
||||
abstracto.featureFlags.poll.featureName=poll
|
||||
abstracto.featureFlags.poll.enabled=false
|
||||
|
||||
abstracto.postTargets.poll.name=polls
|
||||
abstracto.postTargets.pollReminder.name=pollReminder
|
||||
|
||||
abstracto.featureModes.pollAutoEvaluate.featureName=poll
|
||||
abstracto.featureModes.pollAutoEvaluate.mode=pollAutoEvaluate
|
||||
abstracto.featureModes.pollAutoEvaluate.enabled=false
|
||||
|
||||
abstracto.featureModes.pollReminder.featureName=poll
|
||||
abstracto.featureModes.pollReminder.mode=pollReminder
|
||||
abstracto.featureModes.pollReminder.enabled=false
|
||||
|
||||
abstracto.systemConfigs.serverPollDurationSeconds.name=serverPollDurationSeconds
|
||||
abstracto.systemConfigs.serverPollDurationSeconds.longValue=604800
|
||||
|
||||
abstracto.systemConfigs.quickPollDurationSeconds.name=quickPollDurationSeconds
|
||||
abstracto.systemConfigs.quickPollDurationSeconds.longValue=90
|
||||
|
||||
abstracto.feature.poll.removalMaxAge=3600
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>suggestion</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.4.22</version>
|
||||
<version>1.4.26</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package dev.sheldan.abstracto.suggestion.config;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureConfig;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.config.FeatureMode;
|
||||
import dev.sheldan.abstracto.core.config.PostTargetEnum;
|
||||
import dev.sheldan.abstracto.suggestion.service.PollService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class PollFeatureConfig implements FeatureConfig {
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return SuggestionFeatureDefinition.POLL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PostTargetEnum> getRequiredPostTargets() {
|
||||
return Arrays.asList(PollPostTarget.POLLS, PollPostTarget.POLL_REMINDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRequiredSystemConfigKeys() {
|
||||
return Arrays.asList(PollService.SERVER_POLL_DURATION_SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FeatureMode> getAvailableModes() {
|
||||
return Arrays.asList(PollFeatureMode.POLL_AUTO_EVALUATE, PollFeatureMode.POLL_REMINDER);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package dev.sheldan.abstracto.suggestion.config;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureMode;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum PollFeatureMode implements FeatureMode {
|
||||
POLL_AUTO_EVALUATE("pollAutoEvaluate"),
|
||||
POLL_REMINDER("pollReminder");
|
||||
|
||||
private final String key;
|
||||
|
||||
PollFeatureMode(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.suggestion.config;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.PostTargetEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum PollPostTarget implements PostTargetEnum {
|
||||
POLLS("polls"), POLL_REMINDER("pollReminder");
|
||||
|
||||
private String key;
|
||||
|
||||
PollPostTarget(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
@@ -37,13 +37,16 @@ public class SuggestionFeatureConfig implements FeatureConfig {
|
||||
SuggestionFeatureMode.SUGGESTION_REMINDER,
|
||||
SuggestionFeatureMode.SUGGESTION_BUTTONS,
|
||||
SuggestionFeatureMode.SUGGESTION_AUTO_EVALUATE,
|
||||
SuggestionFeatureMode.SUGGESTION_THREAD);
|
||||
SuggestionFeatureMode.SUGGESTION_THREAD
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRequiredSystemConfigKeys() {
|
||||
return Arrays.asList(SuggestionService.SUGGESTION_REMINDER_DAYS_CONFIG_KEY,
|
||||
return Arrays.asList(
|
||||
SuggestionService.SUGGESTION_REMINDER_DAYS_CONFIG_KEY,
|
||||
SuggestionService.SUGGESTION_AUTO_EVALUATE_DAYS_CONFIG_KEY,
|
||||
SuggestionService.SUGGESTION_AUTO_EVALUATE_PERCENTAGE_CONFIG_KEY);
|
||||
SuggestionService.SUGGESTION_AUTO_EVALUATE_PERCENTAGE_CONFIG_KEY
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum SuggestionFeatureDefinition implements FeatureDefinition {
|
||||
SUGGEST("suggestion");
|
||||
SUGGEST("suggestion"), POLL("poll");
|
||||
|
||||
private String key;
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package dev.sheldan.abstracto.suggestion.config;
|
||||
|
||||
public class SuggestionSlashCommandNames {
|
||||
private SuggestionSlashCommandNames() {
|
||||
|
||||
}
|
||||
public static final String SUGGEST = "suggest";
|
||||
public static final String SUGGEST_PUBLIC = "suggestpublic";
|
||||
public static final String POLL_PUBLIC = "pollpublic";
|
||||
public static final String POLL = "poll";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package dev.sheldan.abstracto.suggestion.exception;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.templating.Templatable;
|
||||
|
||||
public class PollCancellationNotPossibleException extends AbstractoRunTimeException implements Templatable {
|
||||
public PollCancellationNotPossibleException() {
|
||||
super("Not possible to cancel poll.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return "poll_cancellation_not_possible_exception";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTemplateModel() {
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package dev.sheldan.abstracto.suggestion.exception;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.templating.Templatable;
|
||||
import dev.sheldan.abstracto.suggestion.model.exception.PollNotFoundExceptionModel;
|
||||
|
||||
public class PollNotFoundException extends AbstractoRunTimeException implements Templatable {
|
||||
private final PollNotFoundExceptionModel model;
|
||||
|
||||
public PollNotFoundException(Long pollId) {
|
||||
super("Poll not found");
|
||||
this.model = PollNotFoundExceptionModel
|
||||
.builder()
|
||||
.pollId(pollId)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return "poll_does_not_exist_exception";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTemplateModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package dev.sheldan.abstracto.suggestion.exception;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.templating.Templatable;
|
||||
|
||||
public class PollOptionAlreadyExistsException extends AbstractoRunTimeException implements Templatable {
|
||||
|
||||
public PollOptionAlreadyExistsException() {
|
||||
super("Poll option already exists.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return "poll_option_already_exists_exception";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTemplateModel() {
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user