mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-01 23:35:29 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
062581fce5 | ||
|
|
be41551529 | ||
|
|
af8206c529 | ||
|
|
8efedf6f6f | ||
|
|
80aff40054 | ||
|
|
c71f5f004d | ||
|
|
350a634f50 | ||
|
|
f5de74c1e4 | ||
|
|
6e3809bfd9 | ||
|
|
8e6339a99b | ||
|
|
befef1f61d | ||
|
|
1f0bc493d9 | ||
|
|
48fa7a99dc | ||
|
|
d12497753d | ||
|
|
20a6e29f1b | ||
|
|
d2231b0934 | ||
|
|
0464afb7db | ||
|
|
c473ca74d4 | ||
|
|
a70ac5aa94 | ||
|
|
d10faf4d3d | ||
|
|
a346e1372c | ||
|
|
9a7c769e8f | ||
|
|
980ca9380c | ||
|
|
e9d14ac417 | ||
|
|
474e632fed | ||
|
|
2e5c2c26b9 | ||
|
|
a8ca9a8055 | ||
|
|
453ccf2e27 | ||
|
|
52c805f4ea | ||
|
|
4b3038078e | ||
|
|
af5f9a8361 | ||
|
|
e84b5ecbb5 | ||
|
|
1ba8219d7b | ||
|
|
60d2c669a2 | ||
|
|
3a122a72e5 | ||
|
|
d53126e5a5 | ||
|
|
208d9a28ed |
@@ -1,2 +1,2 @@
|
||||
REGISTRY_PREFIX=harbor.sheldan.dev/abstracto/
|
||||
VERSION=1.5.11
|
||||
VERSION=1.5.19
|
||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -47,10 +47,9 @@ jobs:
|
||||
id: dotenv
|
||||
uses: falti/dotenv-action@v1.0.4
|
||||
with:
|
||||
path: ./deployment/installer/.env
|
||||
path: .env
|
||||
- name: Push container
|
||||
run: docker-compose build && docker-compose push
|
||||
working-directory: ./deployment/installer/
|
||||
env:
|
||||
REGISTRY_PREFIX: ${{ steps.dotenv.outputs.registry_prefix }}
|
||||
VERSION: ${{ steps.dotenv.outputs.version }}
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>anti-raid</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>anti-raid</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>assignable-roles</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>assignable-roles</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</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.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>custom-command</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -45,8 +45,16 @@ public class CustomCommandAlternative implements CommandAlternative {
|
||||
private CustomCommandFeatureConfig customCommandFeatureConfig;
|
||||
|
||||
@Override
|
||||
public boolean shouldExecute(UnParsedCommandParameter parameter, Guild guild) {
|
||||
return featureFlagService.isFeatureEnabled(customCommandFeatureConfig, guild.getIdLong());
|
||||
public boolean shouldExecute(UnParsedCommandParameter parameter, Guild guild, Message message) {
|
||||
boolean featureEnabled = featureFlagService.isFeatureEnabled(customCommandFeatureConfig, guild.getIdLong());
|
||||
if(featureEnabled) {
|
||||
String contentStripped = message.getContentRaw();
|
||||
List<String> parameters = Arrays.asList(contentStripped.split(" "));
|
||||
String commandName = commandRegistry.getCommandName(parameters.get(0), message.getGuild().getIdLong());
|
||||
Optional<CustomCommand> customCommandOptional = customCommandManagementService.getCustomCommandByName(commandName, message.getGuild().getIdLong());
|
||||
return customCommandOptional.isPresent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>custom-command</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>dynamic-activity</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>dynamic-activity</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,17 +3,12 @@
|
||||
<parent>
|
||||
<artifactId>entertainment</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>entertainment-impl</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@@ -57,6 +52,12 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.scheduling</groupId>
|
||||
<artifactId>scheduling-int</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
package dev.sheldan.abstracto.entertainment.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||
import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.core.utils.ParseUtils;
|
||||
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
|
||||
import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition;
|
||||
import dev.sheldan.abstracto.entertainment.config.EntertainmentSlashCommandNames;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.PressFPromptModel;
|
||||
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureConfig.PRESS_F_DEFAULT_DURATION_SECONDS;
|
||||
|
||||
@Component
|
||||
public class PressFCommand extends AbstractConditionableCommand {
|
||||
|
||||
public static final String TEXT_PARAMETER = "text";
|
||||
public static final String DURATION_PARAMETER = "duration";
|
||||
|
||||
private static final String RESPONSE_TEMPLATE = "pressF_response";
|
||||
public static final String PRESS_F_COMMAND_NAME = "pressF";
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Autowired
|
||||
private ConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private EntertainmentService entertainmentService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
String text = (String) commandContext.getParameters().getParameters().get(0);
|
||||
Long defaultDurationSeconds = configService.getLongValueOrConfigDefault(PRESS_F_DEFAULT_DURATION_SECONDS, commandContext.getGuild().getIdLong());
|
||||
Duration duration = Duration.ofSeconds(defaultDurationSeconds);
|
||||
PressFPromptModel pressFModel = entertainmentService.getPressFModel(text);
|
||||
List<CompletableFuture<Message>> messages = channelService.sendEmbedTemplateInMessageChannelList(RESPONSE_TEMPLATE, pressFModel, commandContext.getChannel());
|
||||
return FutureUtils.toSingleFutureGeneric(messages)
|
||||
.thenAccept(unused -> entertainmentService.persistPressF(text, duration, commandContext.getAuthor(),
|
||||
pressFModel.getPressFComponentId(), commandContext.getChannel(), messages.get(0).join().getIdLong()))
|
||||
.thenApply(unused -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
String text = slashCommandParameterService.getCommandOption(TEXT_PARAMETER, event, String.class);
|
||||
Duration duration;
|
||||
if(slashCommandParameterService.hasCommandOption(DURATION_PARAMETER, event)) {
|
||||
String durationString = slashCommandParameterService.getCommandOption(DURATION_PARAMETER, event, String.class);
|
||||
duration = ParseUtils.parseDuration(durationString);
|
||||
} else {
|
||||
Long defaultDurationSeconds = configService.getLongValueOrConfigDefault(PRESS_F_DEFAULT_DURATION_SECONDS, event.getGuild().getIdLong());
|
||||
duration = Duration.ofSeconds(defaultDurationSeconds);
|
||||
}
|
||||
PressFPromptModel pressFModel = entertainmentService.getPressFModel(text);
|
||||
return interactionService.replyEmbed(RESPONSE_TEMPLATE, pressFModel, event)
|
||||
.thenCompose(interactionHook -> interactionHook.retrieveOriginal().submit())
|
||||
.thenAccept(message -> {
|
||||
entertainmentService.persistPressF(text, duration, event.getMember(), pressFModel.getPressFComponentId(), event.getGuildChannel(), message.getIdLong());
|
||||
})
|
||||
.thenApply(unused -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
Parameter textParameter = Parameter
|
||||
.builder()
|
||||
.name(TEXT_PARAMETER)
|
||||
.type(String.class)
|
||||
.templated(true)
|
||||
.remainder(true)
|
||||
.build();
|
||||
parameters.add(textParameter);
|
||||
Parameter durationParameter = Parameter
|
||||
.builder()
|
||||
.name(DURATION_PARAMETER)
|
||||
.type(Duration.class)
|
||||
.slashCommandOnly(true)
|
||||
.optional(true)
|
||||
.templated(true)
|
||||
.build();
|
||||
parameters.add(durationParameter);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(EntertainmentSlashCommandNames.ENTERTAINMENT)
|
||||
.commandName("pressf")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(PRESS_F_COMMAND_NAME)
|
||||
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
|
||||
.templated(true)
|
||||
.causesReaction(false)
|
||||
.async(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.supportsEmbedException(true)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return EntertainmentFeatureDefinition.ENTERTAINMENT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package dev.sheldan.abstracto.entertainment.job;
|
||||
|
||||
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.DisallowConcurrentExecution;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.PersistJobDataAfterExecution;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.quartz.QuartzJobBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@DisallowConcurrentExecution
|
||||
@Component
|
||||
@PersistJobDataAfterExecution
|
||||
public class PressFEvaluationJob extends QuartzJobBean {
|
||||
@Getter
|
||||
@Setter
|
||||
private Long pressFId;
|
||||
|
||||
@Autowired
|
||||
private EntertainmentService entertainmentService;
|
||||
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext context) {
|
||||
try {
|
||||
log.info("Executing press f evaluation job for pressf instance {}.", pressFId);
|
||||
entertainmentService.evaluatePressF(pressFId);
|
||||
} catch (Exception exception) {
|
||||
log.error("Press f evaluation job failed.", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package dev.sheldan.abstracto.entertainment.listener.interaction;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.config.ListenerPriority;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListener;
|
||||
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerModel;
|
||||
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerResult;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
|
||||
import dev.sheldan.abstracto.entertainment.exception.AlreadyPressedFException;
|
||||
import dev.sheldan.abstracto.entertainment.model.PressFPayload;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.PressFJoinModel;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.PressF;
|
||||
import dev.sheldan.abstracto.entertainment.service.EntertainmentServiceBean;
|
||||
import dev.sheldan.abstracto.entertainment.service.management.PressFManagementService;
|
||||
import dev.sheldan.abstracto.entertainment.service.management.PressFPresserManagementServiceBean;
|
||||
import jakarta.transaction.Transactional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class PressFClickedListener implements ButtonClickedListener {
|
||||
|
||||
private static final String PRESS_F_CLICK_RESPONSE_TEMPLATE_KEY = "pressF_join";
|
||||
|
||||
@Autowired
|
||||
private PressFPresserManagementServiceBean pressFPresserManagementServiceBean;
|
||||
|
||||
@Autowired
|
||||
private PressFManagementService pressFManagementService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private PressFClickedListener self;
|
||||
|
||||
@Override
|
||||
public Boolean handlesEvent(ButtonClickedListenerModel model) {
|
||||
return EntertainmentServiceBean.PRESS_F_BUTTON_ORIGIN.equals(model.getOrigin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
|
||||
PressFPayload payload = (PressFPayload) model.getDeserializedPayload();
|
||||
Optional<PressF> pressFOptional = pressFManagementService.getPressFById(payload.getPressFId());
|
||||
pressFOptional.ifPresent(pressF -> {
|
||||
Member presserMember = model.getEvent().getMember();
|
||||
AUserInAServer presser = userInServerManagementService.loadOrCreateUser(presserMember);
|
||||
Long userInServerId = presser.getUserInServerId();
|
||||
if(!pressFPresserManagementServiceBean.didUserAlreadyPress(pressF, presser)) {
|
||||
PressFJoinModel joinModel = PressFJoinModel
|
||||
.builder()
|
||||
.messageId(pressF.getMessageId())
|
||||
.memberDisplay(MemberDisplay.fromMember(presserMember))
|
||||
.build();
|
||||
interactionService.replyEmbed(PRESS_F_CLICK_RESPONSE_TEMPLATE_KEY, joinModel, model.getEvent().getInteraction()).thenAccept(interactionHook -> {
|
||||
self.persistPresser(payload.getPressFId(), userInServerId);
|
||||
log.info("Send message about pressing to user {} for pressF {}.", presserMember.getIdLong(), payload.getPressFId());
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to send message or persist press user {} in pressF {}.", presserMember.getIdLong(), payload.getPressFId(), throwable);
|
||||
return null;
|
||||
});
|
||||
} else {
|
||||
log.debug("User {} already pressed for pressF {}.", presserMember.getIdLong(), payload.getPressFId());
|
||||
throw new AlreadyPressedFException();
|
||||
}
|
||||
});
|
||||
return ButtonClickedListenerResult.ACKNOWLEDGED;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void persistPresser(Long pressFId, Long userInServerId) {
|
||||
log.info("Persisting pressing of user {} for pressF {}.", userInServerId, pressFId);
|
||||
AUserInAServer presser = userInServerManagementService.loadOrCreateUser(userInServerId);
|
||||
Optional<PressF> pressFByIdOptional = pressFManagementService.getPressFById(pressFId);
|
||||
pressFByIdOptional.ifPresent(pressF -> {
|
||||
pressFPresserManagementServiceBean.addPresser(pressF, presser);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return EntertainmentFeatureDefinition.ENTERTAINMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPriority() {
|
||||
return ListenerPriority.MEDIUM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean autoAcknowledgeEvent() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package dev.sheldan.abstracto.entertainment.model;
|
||||
|
||||
import dev.sheldan.abstracto.core.interaction.button.ButtonPayload;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class PressFPayload implements ButtonPayload {
|
||||
private Long pressFId;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.sheldan.abstracto.entertainment.repository;
|
||||
|
||||
import dev.sheldan.abstracto.entertainment.model.database.PressFPresser;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.embed.PressFPresserId;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface PressFPresserRepository extends JpaRepository<PressFPresser, PressFPresserId> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package dev.sheldan.abstracto.entertainment.repository;
|
||||
|
||||
import dev.sheldan.abstracto.entertainment.model.database.PressF;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface PressFRepository extends JpaRepository<PressF, Long> {
|
||||
}
|
||||
@@ -2,12 +2,33 @@ package dev.sheldan.abstracto.entertainment.service;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentPayloadService;
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentService;
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.abstracto.core.service.MessageService;
|
||||
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureConfig;
|
||||
import dev.sheldan.abstracto.entertainment.exception.ReactDuplicateCharacterException;
|
||||
import dev.sheldan.abstracto.entertainment.model.PressFPayload;
|
||||
import dev.sheldan.abstracto.entertainment.model.ReactMapping;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.PressFPromptModel;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.PressFResultModel;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.PressF;
|
||||
import dev.sheldan.abstracto.entertainment.service.management.PressFManagementService;
|
||||
import dev.sheldan.abstracto.scheduling.model.JobParameters;
|
||||
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
|
||||
import jakarta.transaction.Transactional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -17,7 +38,10 @@ import javax.annotation.PostConstruct;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@@ -30,6 +54,9 @@ public class EntertainmentServiceBean implements EntertainmentService {
|
||||
"DONT_COUNT", "REPLY_NO", "SOURCES_NO", "OUTLOOK_NOT_GOOD", "DOUBTFUL" // negative
|
||||
);
|
||||
|
||||
public static final String PRESS_F_BUTTON_ORIGIN = "PRESS_F_BUTTON";
|
||||
private static final String PRESS_F_RESULT_TEMPLATE_KEY = "pressF_result";
|
||||
|
||||
private ReactMapping reactMapping;
|
||||
|
||||
@Autowired
|
||||
@@ -38,6 +65,33 @@ public class EntertainmentServiceBean implements EntertainmentService {
|
||||
@Autowired
|
||||
private ConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private ComponentService componentService;
|
||||
|
||||
@Autowired
|
||||
private PressFManagementService pressFManagementService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private SchedulerService schedulerService;
|
||||
|
||||
@Autowired
|
||||
private ComponentPayloadService componentPayloadService;
|
||||
|
||||
@Autowired
|
||||
private ChannelManagementService channelManagementService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private MessageService messageService;
|
||||
|
||||
@Value("classpath:react_mappings.json")
|
||||
private Resource reactMappingSource;
|
||||
|
||||
@@ -91,6 +145,66 @@ public class EntertainmentServiceBean implements EntertainmentService {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PressFPromptModel getPressFModel(String text) {
|
||||
String pressFComponent = componentService.generateComponentId();
|
||||
return PressFPromptModel
|
||||
.builder()
|
||||
.pressFComponentId(pressFComponent)
|
||||
.text(text)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void persistPressF(String text, Duration duration, Member executingMember, String componentId, GuildMessageChannel guildMessageChannel, Long messageId) {
|
||||
Instant targetDate = Instant.now().plus(duration);
|
||||
log.info("Persisting pressF started by {} in server {} with due date {}.", executingMember.getIdLong(), executingMember.getGuild().getIdLong(), targetDate);
|
||||
AUserInAServer creator = userInServerManagementService.loadOrCreateUser(executingMember);
|
||||
AChannel channel = channelManagementService.loadChannel(guildMessageChannel);
|
||||
PressF pressF = pressFManagementService.createPressF(text, targetDate, creator, channel, messageId);
|
||||
HashMap<Object, Object> parameters = new HashMap<>();
|
||||
parameters.put("pressFId", pressF.getId().toString());
|
||||
JobParameters jobParameters = JobParameters
|
||||
.builder()
|
||||
.parameters(parameters)
|
||||
.build();
|
||||
log.debug("Starting scheduled job for pressF {}", pressF.getId());
|
||||
schedulerService.executeJobWithParametersOnce("pressFEvaluationJob", "entertainment", jobParameters, Date.from(targetDate));
|
||||
PressFPayload pressFPayload = PressFPayload
|
||||
.builder()
|
||||
.pressFId(pressF.getId())
|
||||
.build();
|
||||
log.debug("Persisting payload for pressF {}", pressF.getId());
|
||||
componentPayloadService.createButtonPayload(componentId, pressFPayload, PRESS_F_BUTTON_ORIGIN, creator.getServerReference());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CompletableFuture<Void> evaluatePressF(Long pressFId) {
|
||||
Optional<PressF> pressFOptional = pressFManagementService.getPressFById(pressFId);
|
||||
if(pressFOptional.isPresent()) {
|
||||
log.info("Evaluating pressF with id {}", pressFId);
|
||||
PressF pressF = pressFOptional.get();
|
||||
PressFResultModel model = PressFResultModel
|
||||
.builder()
|
||||
.userCount((long) pressF.getPresser().size())
|
||||
.text(pressF.getText())
|
||||
.messageId(pressF.getMessageId())
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(PRESS_F_RESULT_TEMPLATE_KEY, model);
|
||||
Long serverId = pressF.getServer().getId();
|
||||
Long channelId = pressF.getPressFChannel().getId();
|
||||
Long messageId = pressF.getMessageId();
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageEmbedToSendToAChannel(messageToSend, pressF.getPressFChannel()))
|
||||
.thenCompose(unused -> messageService.loadMessage(serverId, channelId, messageId).thenCompose(message -> {
|
||||
log.info("Clearing buttons from pressF {} in with message {} in channel {} in server {}.", pressFId, pressFId, channelId, serverId);
|
||||
return componentService.clearButtons(message);
|
||||
}));
|
||||
} else {
|
||||
throw new AbstractoRunTimeException(String.format("PressF with id %s not found.", pressFId));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> convertTextToEmojis(String text) {
|
||||
return convertTextToEmojis(text, false);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package dev.sheldan.abstracto.entertainment.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.PressF;
|
||||
import dev.sheldan.abstracto.entertainment.repository.PressFRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class PressFManagementServiceBean implements PressFManagementService {
|
||||
|
||||
@Autowired
|
||||
private PressFRepository pressFRepository;
|
||||
@Override
|
||||
public PressF createPressF(String text, Instant targetDate, AUserInAServer creator, AChannel channel, Long messageId) {
|
||||
PressF pressF = PressF
|
||||
.builder()
|
||||
.server(creator.getServerReference())
|
||||
.creator(creator)
|
||||
.messageId(messageId)
|
||||
.pressFChannel(channel)
|
||||
.text(text)
|
||||
.targetDate(targetDate)
|
||||
.build();
|
||||
return pressFRepository.save(pressF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<PressF> getPressFById(Long pressFId) {
|
||||
return pressFRepository.findById(pressFId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package dev.sheldan.abstracto.entertainment.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.PressF;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.PressFPresser;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.embed.PressFPresserId;
|
||||
import dev.sheldan.abstracto.entertainment.repository.PressFPresserRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class PressFPresserManagementServiceBean implements PressFPresserManagementService {
|
||||
|
||||
@Autowired
|
||||
private PressFPresserRepository repository;
|
||||
|
||||
@Override
|
||||
public PressFPresser addPresser(PressF pressF, AUserInAServer presser) {
|
||||
PressFPresser pressFPresser = PressFPresser
|
||||
.builder()
|
||||
.presser(presser)
|
||||
.id(new PressFPresserId(presser.getUserInServerId(), pressF.getId()))
|
||||
.build();
|
||||
return repository.save(pressFPresser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean didUserAlreadyPress(PressF pressF, AUserInAServer aUserInAServer) {
|
||||
return repository.existsById(new PressFPresserId(aUserInAServer.getUserInServerId(), pressF.getId()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,9 @@ abstracto.systemConfigs.rouletteBullets.longValue=6
|
||||
abstracto.systemConfigs.rollDefaultHigh.name=rollDefaultHigh
|
||||
abstracto.systemConfigs.rollDefaultHigh.longValue=6
|
||||
|
||||
abstracto.systemConfigs.pressFDefaultDurationSeconds.name=pressFDefaultDurationSeconds
|
||||
abstracto.systemConfigs.pressFDefaultDurationSeconds.longValue=90
|
||||
|
||||
abstracto.featureFlags.entertainment.featureName=entertainment
|
||||
abstracto.featureFlags.entertainment.enabled=false
|
||||
|
||||
|
||||
@@ -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,20 @@
|
||||
<?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="entertainmentModule" value="(SELECT id FROM module WHERE name = 'entertainment')"/>
|
||||
<property name="entertainmentFeature" value="(SELECT id FROM feature WHERE key = 'entertainment')"/>
|
||||
|
||||
<changeSet author="Sheldan" id="pressF-commands">
|
||||
<insert tableName="command">
|
||||
<column name="name" value="pressF"/>
|
||||
<column name="module_id" valueComputed="${entertainmentModule}"/>
|
||||
<column name="feature_id" valueComputed="${entertainmentFeature}"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -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="command.xml" relativeToChangelogFile="true"/>
|
||||
<include file="press_f_evaluation_job.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<changeSet author="Sheldan" id="press_f_evaluation_job-insert">
|
||||
<insert tableName="scheduler_job">
|
||||
<column name="name" value="pressFEvaluationJob"/>
|
||||
<column name="group_name" value="entertainment"/>
|
||||
<column name="clazz" value="dev.sheldan.abstracto.entertainment.job.PressFEvaluationJob"/>
|
||||
<column name="active" value="true"/>
|
||||
<column name="recovery" value="false"/>
|
||||
</insert>
|
||||
</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="press_f-table">
|
||||
<createTable tableName="press_f">
|
||||
<column name="id" autoIncrement="true" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true" primaryKeyName="press_f_pkey"/>
|
||||
</column>
|
||||
<column name="creator_user_id" type="INTEGER">
|
||||
<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="target_date" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="text" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="press_f_channel_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="message_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseColumnNames="creator_user_id" baseTableName="press_f" constraintName="fk_press_f_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="press_f" constraintName="fk_press_f_server"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="press_f_channel_id" baseTableName="press_f" constraintName="fk_press_f_channel"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="channel" validate="true"/>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS press_f_update_trigger ON press_f;
|
||||
CREATE TRIGGER press_f_update_trigger BEFORE UPDATE ON press_f FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS press_f_insert_trigger ON press_f;
|
||||
CREATE TRIGGER press_f_insert_trigger BEFORE INSERT ON press_f FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?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="press_f_presser-table">
|
||||
<createTable tableName="press_f_presser">
|
||||
<column name="press_f_presser_user_in_server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="press_f_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
|
||||
</createTable>
|
||||
<addPrimaryKey columnNames="press_f_presser_user_in_server_id, press_f_id" tableName="press_f_presser" constraintName="pk_press_f_presser" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="press_f_presser_user_in_server_id" baseTableName="press_f_presser" constraintName="fk_press_f_presser_user"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id"
|
||||
referencedTableName="user_in_server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="press_f_id" baseTableName="press_f_presser" constraintName="fk_press_f_presser_press_f"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
|
||||
referencedTableName="press_f" validate="true"/>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS press_f_presser_update_trigger ON press_f_presser;
|
||||
CREATE TRIGGER press_f_presser_update_trigger BEFORE UPDATE ON press_f_presser FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS press_f_presser_insert_trigger ON press_f_presser;
|
||||
CREATE TRIGGER press_f_presser_insert_trigger BEFORE INSERT ON press_f_presser FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -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="press_f.xml" relativeToChangelogFile="true"/>
|
||||
<include file="press_f_presser.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -13,4 +13,5 @@
|
||||
<include file="1.4.3/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.4.11/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.5.8/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.5.14/collection.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -3,15 +3,10 @@
|
||||
<parent>
|
||||
<artifactId>entertainment</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>entertainment-int</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -12,6 +12,7 @@ public class EntertainmentFeatureConfig implements FeatureConfig {
|
||||
|
||||
public static final String ROULETTE_BULLETS_CONFIG_KEY = "rouletteBullets";
|
||||
public static final String ROLL_DEFAULT_HIGH_KEY = "rollDefaultHigh";
|
||||
public static final String PRESS_F_DEFAULT_DURATION_SECONDS = "pressFDefaultDurationSeconds";
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return EntertainmentFeatureDefinition.ENTERTAINMENT;
|
||||
@@ -19,6 +20,6 @@ public class EntertainmentFeatureConfig implements FeatureConfig {
|
||||
|
||||
@Override
|
||||
public List<String> getRequiredSystemConfigKeys() {
|
||||
return Arrays.asList(ROULETTE_BULLETS_CONFIG_KEY, ROLL_DEFAULT_HIGH_KEY);
|
||||
return Arrays.asList(ROULETTE_BULLETS_CONFIG_KEY, ROLL_DEFAULT_HIGH_KEY, PRESS_F_DEFAULT_DURATION_SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.entertainment.exception;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
|
||||
|
||||
public class AlreadyPressedFException extends AbstractoTemplatableException {
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return "already_pressed_f_exception";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTemplateModel() {
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Builder
|
||||
@Setter
|
||||
@Getter
|
||||
public class PressFJoinModel {
|
||||
private MemberDisplay memberDisplay;
|
||||
private Long messageId;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.command;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Builder
|
||||
@Setter
|
||||
@Getter
|
||||
public class PressFPromptModel {
|
||||
private String text;
|
||||
private String pressFComponentId;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.command;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Builder
|
||||
@Setter
|
||||
@Getter
|
||||
public class PressFResultModel {
|
||||
private Long userCount;
|
||||
private String text;
|
||||
private Long messageId;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.database;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Builder
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "press_f")
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
public class PressF {
|
||||
@Id
|
||||
@Column(name = "id", nullable = false)
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "creator_user_id", nullable = false)
|
||||
private AUserInAServer creator;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "server_id", nullable = false)
|
||||
private AServer server;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "press_f_channel_id", nullable = false)
|
||||
private AChannel pressFChannel;
|
||||
|
||||
@Column(name = "message_id", nullable = false)
|
||||
private Long messageId;
|
||||
|
||||
@Column(name = "text")
|
||||
private String text;
|
||||
|
||||
@Column(name = "target_date", nullable = false)
|
||||
private Instant targetDate;
|
||||
|
||||
@Column(name = "created", nullable = false, insertable = false, updatable = false)
|
||||
private Instant created;
|
||||
|
||||
@Column(name = "updated", insertable = false, updatable = false)
|
||||
private Instant updated;
|
||||
|
||||
@OneToMany(
|
||||
fetch = FetchType.LAZY,
|
||||
orphanRemoval = true,
|
||||
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
|
||||
mappedBy = "pressF")
|
||||
@Builder.Default
|
||||
private List<PressFPresser> presser = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.database;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.embed.PressFPresserId;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@Builder
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "press_f_presser")
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
public class PressFPresser {
|
||||
|
||||
@EmbeddedId
|
||||
@Getter
|
||||
private PressFPresserId id;
|
||||
|
||||
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
|
||||
@MapsId("presserId")
|
||||
@JoinColumn(name = "press_f_presser_user_in_server_id", nullable = false)
|
||||
private AUserInAServer presser;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(updatable = false, insertable = false, name = "press_f_id", referencedColumnName = "id")
|
||||
private PressF pressF;
|
||||
|
||||
@Column(name = "created", nullable = false, insertable = false, updatable = false)
|
||||
private Instant created;
|
||||
|
||||
@Column(name = "updated", insertable = false, updatable = false)
|
||||
private Instant updated;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.database.embed;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import lombok.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Embeddable
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@EqualsAndHashCode
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PressFPresserId implements Serializable {
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "press_f_presser_user_in_server_id")
|
||||
private Long presserId;
|
||||
|
||||
@Column(name = "press_f_id")
|
||||
private Long pressFId;
|
||||
|
||||
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
package dev.sheldan.abstracto.entertainment.service;
|
||||
|
||||
import dev.sheldan.abstracto.entertainment.exception.ReactDuplicateCharacterException;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.PressFPromptModel;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface EntertainmentService {
|
||||
String getEightBallValue(String text);
|
||||
@@ -12,6 +16,10 @@ public interface EntertainmentService {
|
||||
boolean executeRoulette(Member memberExecuting);
|
||||
String takeChoice(List<String> choices, Member memberExecuting);
|
||||
String createMockText(String text, Member memberExecuting, Member mockedUser);
|
||||
PressFPromptModel getPressFModel(String text);
|
||||
void persistPressF(String text, Duration duration, Member executingMember, String componentId, GuildMessageChannel guildMessageChannel, Long messageId);
|
||||
|
||||
CompletableFuture<Void> evaluatePressF(Long pressFId);
|
||||
|
||||
/**
|
||||
* Converts the given text to unicode characters (with predefined values from a manual mapping) and returns the matched
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package dev.sheldan.abstracto.entertainment.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.PressF;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface PressFManagementService {
|
||||
PressF createPressF(String text, Instant targetDate, AUserInAServer creator, AChannel channel, Long messageId);
|
||||
Optional<PressF> getPressFById(Long pressFId);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.sheldan.abstracto.entertainment.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.PressF;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.PressFPresser;
|
||||
|
||||
public interface PressFPresserManagementService {
|
||||
PressFPresser addPresser(PressF pressF, AUserInAServer presser);
|
||||
boolean didUserAlreadyPress(PressF pressF, AUserInAServer aUserInAServer);
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -23,9 +23,4 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>experience-tracking</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>experience-tracking</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.5.11</version>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>giveaway</artifactId>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>giveaway-impl</artifactId>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/liquibase.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>giveaway-int</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.core</groupId>
|
||||
<artifactId>metrics-int</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,18 @@
|
||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
|
||||
<id>liquibase</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
<directory>${project.basedir}/src/main/resources/migrations</directory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
@@ -0,0 +1,103 @@
|
||||
package dev.sheldan.abstracto.giveaway.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||
import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
|
||||
import dev.sheldan.abstracto.giveaway.config.GiveawayFeatureDefinition;
|
||||
import dev.sheldan.abstracto.giveaway.config.GiveawaySlashCommandNames;
|
||||
import dev.sheldan.abstracto.giveaway.service.GiveawayService;
|
||||
import jakarta.transaction.Transactional;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class CancelGiveaway extends AbstractConditionableCommand {
|
||||
|
||||
private static final String COMMAND_NAME = "cancelGiveaway";
|
||||
private static final String ID_PARAMETER = "id";
|
||||
private static final String CANCEL_GIVEAWAY_RESPONSE_TEMPLATE_KEY = "cancelGiveaway_response";
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Autowired
|
||||
private GiveawayService giveawayService;
|
||||
|
||||
@Autowired
|
||||
private CancelGiveaway self;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
return event.deferReply().submit().thenAccept(interactionHook -> {
|
||||
self.cancelGiveaway(event);
|
||||
}).thenCompose(unused -> {
|
||||
List<CompletableFuture<Message>> futures = interactionService.sendMessageToInteraction(CANCEL_GIVEAWAY_RESPONSE_TEMPLATE_KEY, new Object(), event.getHook());
|
||||
return new CompletableFutureList<>(futures).getMainFuture();
|
||||
})
|
||||
.thenApply(unused -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void cancelGiveaway(SlashCommandInteractionEvent event) {
|
||||
Long giveawayId = slashCommandParameterService.getCommandOption(ID_PARAMETER, event, Integer.class).longValue();
|
||||
giveawayService.cancelGiveaway(giveawayId, event.getGuild().getIdLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
|
||||
Parameter giveawayIdParameter = Parameter
|
||||
.builder()
|
||||
.templated(true)
|
||||
.name(ID_PARAMETER)
|
||||
.type(Integer.class)
|
||||
.build();
|
||||
|
||||
List<Parameter> parameters = Arrays.asList(giveawayIdParameter);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(GiveawaySlashCommandNames.GIVEAWAY)
|
||||
.commandName("cancel")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(COMMAND_NAME)
|
||||
.module(UtilityModuleDefinition.UTILITY)
|
||||
.templated(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.async(true)
|
||||
.supportsEmbedException(true)
|
||||
.causesReaction(false)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return GiveawayFeatureDefinition.GIVEAWAY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package dev.sheldan.abstracto.giveaway.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||
import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
import dev.sheldan.abstracto.core.utils.ParseUtils;
|
||||
import dev.sheldan.abstracto.giveaway.config.GiveawayFeatureDefinition;
|
||||
import dev.sheldan.abstracto.giveaway.config.GiveawaySlashCommandNames;
|
||||
import dev.sheldan.abstracto.giveaway.model.GiveawayCreationRequest;
|
||||
import dev.sheldan.abstracto.giveaway.service.GiveawayService;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class GreateGiveaway extends AbstractConditionableCommand {
|
||||
|
||||
private static final String COMMAND_NAME = "createGiveaway";
|
||||
private static final String TITLE_PARAMETER = "title";
|
||||
private static final String DESCRIPTION_PARAMETER = "description";
|
||||
private static final String BENEFACTOR_PARAMETER = "benefactor";
|
||||
private static final String CHANNEL_PARAMETER = "channel";
|
||||
private static final String DURATION_PARAMETER = "duration";
|
||||
private static final String WINNERS_PARAMETER = "winners";
|
||||
|
||||
private static final String CREATE_GIVEAWAY_RESPONSE_TEMPLATE_KEY = "createGiveaway_response";
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Autowired
|
||||
private GiveawayService giveawayService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
return event.deferReply()
|
||||
.submit()
|
||||
.thenCompose(interactionHook -> {
|
||||
String title = slashCommandParameterService.getCommandOption(TITLE_PARAMETER, event, String.class);
|
||||
String description;
|
||||
if(slashCommandParameterService.hasCommandOption(DESCRIPTION_PARAMETER, event)) {
|
||||
description = slashCommandParameterService.getCommandOption(DESCRIPTION_PARAMETER, event, String.class);
|
||||
} else {
|
||||
description = null;
|
||||
}
|
||||
|
||||
String durationString = slashCommandParameterService.getCommandOption(DURATION_PARAMETER, event, Duration.class, String.class);
|
||||
Duration duration = ParseUtils.parseDuration(durationString);
|
||||
|
||||
GuildMessageChannel target = null;
|
||||
if(slashCommandParameterService.hasCommandOption(CHANNEL_PARAMETER, event)) {
|
||||
target = slashCommandParameterService.getCommandOption(CHANNEL_PARAMETER, event, GuildMessageChannel.class);
|
||||
}
|
||||
|
||||
Integer winners = 1;
|
||||
if(slashCommandParameterService.hasCommandOption(WINNERS_PARAMETER, event)) {
|
||||
winners = slashCommandParameterService.getCommandOption(WINNERS_PARAMETER, event, Integer.class);
|
||||
}
|
||||
|
||||
Member benefactor;
|
||||
if(slashCommandParameterService.hasCommandOption(BENEFACTOR_PARAMETER, event)) {
|
||||
benefactor = slashCommandParameterService.getCommandOption(BENEFACTOR_PARAMETER, event, Member.class);
|
||||
} else {
|
||||
benefactor = null;
|
||||
}
|
||||
|
||||
Member creator = event.getMember();
|
||||
GiveawayCreationRequest request = GiveawayCreationRequest
|
||||
.builder()
|
||||
.benefactor(benefactor)
|
||||
.creator(creator)
|
||||
.description(description)
|
||||
.duration(duration)
|
||||
.targetChannel(target)
|
||||
.winnerCount(winners)
|
||||
.title(title)
|
||||
.build();
|
||||
|
||||
return giveawayService.createGiveaway(request)
|
||||
.thenAccept(unused -> interactionService.sendEmbed(CREATE_GIVEAWAY_RESPONSE_TEMPLATE_KEY, interactionHook));
|
||||
}).thenApply(unused -> CommandResult.fromSuccess());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
|
||||
Parameter titleParameter = Parameter
|
||||
.builder()
|
||||
.templated(true)
|
||||
.name(TITLE_PARAMETER)
|
||||
.type(String.class)
|
||||
.build();
|
||||
|
||||
Parameter descriptionParameter = Parameter
|
||||
.builder()
|
||||
.templated(true)
|
||||
.name(DESCRIPTION_PARAMETER)
|
||||
.type(String.class)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
Parameter channelParameter = Parameter
|
||||
.builder()
|
||||
.name(CHANNEL_PARAMETER)
|
||||
.type(GuildMessageChannel.class)
|
||||
.optional(true)
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
Parameter durationParameter = Parameter
|
||||
.builder()
|
||||
.name(DURATION_PARAMETER)
|
||||
.type(Duration.class)
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
Parameter winnersParameter = Parameter
|
||||
.builder()
|
||||
.name(WINNERS_PARAMETER)
|
||||
.type(Integer.class)
|
||||
.optional(true)
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
Parameter benefactorParameter = Parameter
|
||||
.builder()
|
||||
.templated(true)
|
||||
.name(BENEFACTOR_PARAMETER)
|
||||
.type(Member.class)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
List<Parameter> parameters = Arrays.asList(titleParameter, durationParameter, benefactorParameter, descriptionParameter,
|
||||
channelParameter, winnersParameter);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(GiveawaySlashCommandNames.GIVEAWAY)
|
||||
.commandName("create")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(COMMAND_NAME)
|
||||
.module(UtilityModuleDefinition.UTILITY)
|
||||
.templated(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.async(true)
|
||||
.supportsEmbedException(true)
|
||||
.causesReaction(false)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return GiveawayFeatureDefinition.GIVEAWAY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package dev.sheldan.abstracto.giveaway.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||
import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
|
||||
import dev.sheldan.abstracto.giveaway.config.GiveawayFeatureDefinition;
|
||||
import dev.sheldan.abstracto.giveaway.config.GiveawaySlashCommandNames;
|
||||
import dev.sheldan.abstracto.giveaway.service.GiveawayService;
|
||||
import jakarta.transaction.Transactional;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class ReRollGiveaway extends AbstractConditionableCommand {
|
||||
|
||||
private static final String COMMAND_NAME = "reRollGiveaway";
|
||||
private static final String ID_PARAMETER = "id";
|
||||
private static final String RE_ROLL_GIVEAWAY_RESPONSE_TEMPLATE_KEY = "reRollGiveaway_response";
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Autowired
|
||||
private GiveawayService giveawayService;
|
||||
|
||||
@Autowired
|
||||
private ReRollGiveaway self;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
return event.deferReply().submit().thenAccept(interactionHook -> {
|
||||
self.reRollGiveaway(event);
|
||||
}).thenCompose(unused -> {
|
||||
List<CompletableFuture<Message>> futures = interactionService.sendMessageToInteraction(RE_ROLL_GIVEAWAY_RESPONSE_TEMPLATE_KEY, new Object(), event.getHook());
|
||||
return new CompletableFutureList<>(futures).getMainFuture();
|
||||
})
|
||||
.thenApply(unused -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void reRollGiveaway(SlashCommandInteractionEvent event) {
|
||||
Long giveawayId = slashCommandParameterService.getCommandOption(ID_PARAMETER, event, Integer.class).longValue();
|
||||
giveawayService.evaluateGiveaway(giveawayId, event.getGuild().getIdLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
|
||||
Parameter giveawayIdParameter = Parameter
|
||||
.builder()
|
||||
.templated(true)
|
||||
.name(ID_PARAMETER)
|
||||
.type(Integer.class)
|
||||
.build();
|
||||
|
||||
List<Parameter> parameters = Arrays.asList(giveawayIdParameter);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(GiveawaySlashCommandNames.GIVEAWAY)
|
||||
.commandName("reroll")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(COMMAND_NAME)
|
||||
.module(UtilityModuleDefinition.UTILITY)
|
||||
.templated(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.async(true)
|
||||
.supportsEmbedException(true)
|
||||
.causesReaction(false)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return GiveawayFeatureDefinition.GIVEAWAY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.sheldan.abstracto.giveaway.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
@Configuration
|
||||
@PropertySource("classpath:giveaway-config.properties")
|
||||
public class GiveawayConfig {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package dev.sheldan.abstracto.giveaway.job;
|
||||
|
||||
import dev.sheldan.abstracto.giveaway.service.GiveawayService;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.DisallowConcurrentExecution;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.PersistJobDataAfterExecution;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.quartz.QuartzJobBean;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@DisallowConcurrentExecution
|
||||
@Component
|
||||
@PersistJobDataAfterExecution
|
||||
public class GiveawayEvaluationJob extends QuartzJobBean {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private Long giveawayId;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private Long serverId;
|
||||
|
||||
@Autowired
|
||||
private GiveawayService giveawayService;
|
||||
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext context) {
|
||||
try {
|
||||
log.info("Executing giveaway evaluation job for giveaway {} in server {}", giveawayId, serverId);
|
||||
giveawayService.evaluateGiveaway(giveawayId, serverId);
|
||||
} catch (Exception exception) {
|
||||
log.error("Giveaway evaluation job failed.", exception);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package dev.sheldan.abstracto.giveaway.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.config.ListenerPriority;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListener;
|
||||
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerModel;
|
||||
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerResult;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.giveaway.config.GiveawayFeatureDefinition;
|
||||
import dev.sheldan.abstracto.giveaway.exception.GiveawayNotFoundException;
|
||||
import dev.sheldan.abstracto.giveaway.model.JoinGiveawayPayload;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
|
||||
import dev.sheldan.abstracto.giveaway.service.GiveawayService;
|
||||
import dev.sheldan.abstracto.giveaway.service.GiveawayServiceBean;
|
||||
import dev.sheldan.abstracto.giveaway.service.management.GiveawayManagementService;
|
||||
import dev.sheldan.abstracto.giveaway.service.management.GiveawayParticipantManagementService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class GiveawayJoinListener implements ButtonClickedListener {
|
||||
|
||||
private static final String GIVEAWAY_JOIN_RESPONSE_TEMPLATE_KEY = "giveaway_join_response";
|
||||
private static final String GIVEAWAY_ALREADY_JOINED_RESPONSE_TEMPLATE_KEY = "giveaway_already_joined_response";
|
||||
|
||||
@Autowired
|
||||
private GiveawayManagementService giveawayManagementService;
|
||||
|
||||
@Autowired
|
||||
private GiveawayService giveawayService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private GiveawayParticipantManagementService giveawayParticipantManagementService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Override
|
||||
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
|
||||
JoinGiveawayPayload payload = (JoinGiveawayPayload) model.getDeserializedPayload();
|
||||
Optional<Giveaway> optionalGiveaway = giveawayManagementService.loadGiveawayById(payload.getGiveawayId(), payload.getServerId());
|
||||
if(optionalGiveaway.isPresent()) {
|
||||
Giveaway giveaway = optionalGiveaway.get();
|
||||
AUserInAServer user = userInServerManagementService.loadOrCreateUser(model.getEvent().getMember());
|
||||
Long joiningUserId = model.getEvent().getMember().getIdLong();
|
||||
if(!giveawayParticipantManagementService.userIsAlreadyParticipating(giveaway, user)) {
|
||||
log.info("Adding user {} to giveaway {}.", joiningUserId, payload.getGiveawayId());
|
||||
giveawayService.addGiveawayParticipant(giveaway, model.getEvent().getMember(), model.getEvent().getMessageChannel())
|
||||
.thenAccept(unused -> {
|
||||
log.info("Notified user {} in giveaway {} join event.", joiningUserId, payload.getGiveawayId());
|
||||
interactionService.replyEmbed(GIVEAWAY_JOIN_RESPONSE_TEMPLATE_KEY, model.getEvent());
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to add {} to giveaway {}.", joiningUserId, payload.getGiveawayId(), throwable);
|
||||
return null;
|
||||
});
|
||||
} else {
|
||||
log.info("User {} was already part of giveaway {}.", joiningUserId, payload.getGiveawayId());
|
||||
interactionService.replyEmbed(GIVEAWAY_ALREADY_JOINED_RESPONSE_TEMPLATE_KEY, model.getEvent());
|
||||
}
|
||||
return ButtonClickedListenerResult.ACKNOWLEDGED;
|
||||
} else {
|
||||
throw new GiveawayNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean handlesEvent(ButtonClickedListenerModel model) {
|
||||
return GiveawayServiceBean.GIVEAWAY_JOIN_ORIGIN.equals(model.getOrigin());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return GiveawayFeatureDefinition.GIVEAWAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPriority() {
|
||||
return ListenerPriority.MEDIUM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean autoAcknowledgeEvent() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.sheldan.abstracto.giveaway.repository;
|
||||
|
||||
import dev.sheldan.abstracto.giveaway.model.database.GiveawayParticipant;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.embed.GiveawayParticipationId;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface GiveawayParticipantRepository extends JpaRepository<GiveawayParticipant, GiveawayParticipationId> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.sheldan.abstracto.giveaway.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerSpecificId;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface GiveawayRepository extends JpaRepository<Giveaway, ServerSpecificId> {
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
package dev.sheldan.abstracto.giveaway.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentPayloadService;
|
||||
import dev.sheldan.abstracto.core.interaction.ComponentService;
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.CounterService;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
|
||||
import dev.sheldan.abstracto.giveaway.config.GiveawayPostTarget;
|
||||
import dev.sheldan.abstracto.giveaway.exception.GiveawayNotFoundException;
|
||||
import dev.sheldan.abstracto.giveaway.model.GiveawayCreationRequest;
|
||||
import dev.sheldan.abstracto.giveaway.model.JoinGiveawayPayload;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.GiveawayParticipant;
|
||||
import dev.sheldan.abstracto.giveaway.model.template.GiveawayMessageModel;
|
||||
import dev.sheldan.abstracto.giveaway.model.template.GiveawayResultMessageModel;
|
||||
import dev.sheldan.abstracto.giveaway.service.management.GiveawayManagementService;
|
||||
import dev.sheldan.abstracto.giveaway.service.management.GiveawayParticipantManagementService;
|
||||
import dev.sheldan.abstracto.scheduling.model.JobParameters;
|
||||
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
|
||||
import jakarta.transaction.Transactional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class GiveawayServiceBean implements GiveawayService {
|
||||
|
||||
private static final String GIVEAWAY_MESSAGE_TEMPLATE_KEY = "giveaway_post";
|
||||
private static final String GIVEAWAY_RESULT_MESSAGE_TEMPLATE_KEY = "giveaway_result";
|
||||
public static final String GIVEAWAY_JOIN_ORIGIN = "JOIN_GIVEAWAY";
|
||||
|
||||
public static final String GIVEAWAY_COUNTER = "giveaways";
|
||||
|
||||
@Autowired
|
||||
private ChannelManagementService channelManagementService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private GiveawayManagementService giveawayManagementService;
|
||||
|
||||
@Autowired
|
||||
private ComponentService componentService;
|
||||
|
||||
@Autowired
|
||||
private PostTargetService postTargetService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private ComponentPayloadService componentPayloadService;
|
||||
|
||||
@Autowired
|
||||
private GiveawayParticipantManagementService giveawayParticipantManagementService;
|
||||
|
||||
@Autowired
|
||||
private SchedulerService schedulerService;
|
||||
|
||||
@Autowired
|
||||
private SecureRandom secureRandom;
|
||||
|
||||
@Autowired
|
||||
private CounterService counterService;
|
||||
|
||||
@Autowired
|
||||
private GiveawayServiceBean self;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> createGiveaway(GiveawayCreationRequest giveawayCreationRequest) {
|
||||
String componentId = componentService.generateComponentId();
|
||||
Instant targetDate = Instant.now().plus(giveawayCreationRequest.getDuration());
|
||||
Long serverId = giveawayCreationRequest.getCreator().getGuild().getIdLong();
|
||||
Long giveawayId = counterService.getNextCounterValue(serverId, GIVEAWAY_COUNTER);
|
||||
GiveawayMessageModel model = GiveawayMessageModel
|
||||
.builder()
|
||||
.title(giveawayCreationRequest.getTitle())
|
||||
.description(giveawayCreationRequest.getDescription())
|
||||
.giveawayId(giveawayId)
|
||||
.benefactor(giveawayCreationRequest.getBenefactor() != null ? MemberDisplay.fromMember(giveawayCreationRequest.getBenefactor()) : null)
|
||||
.creator(MemberDisplay.fromMember(giveawayCreationRequest.getCreator()))
|
||||
.winnerCount(giveawayCreationRequest.getWinnerCount())
|
||||
.targetDate(targetDate)
|
||||
.joinComponentId(componentId)
|
||||
.build();
|
||||
List<CompletableFuture<Message>> messageFutures;
|
||||
log.info("Rendering giveaway message in server {} by user {}", serverId, giveawayCreationRequest.getCreator().getIdLong());
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, model, serverId);
|
||||
if(giveawayCreationRequest.getTargetChannel() == null) {
|
||||
log.info("Sending giveaway to post target in server {}", serverId);
|
||||
postTargetService.validatePostTarget(GiveawayPostTarget.GIVEAWAYS, giveawayCreationRequest.getCreator().getGuild().getIdLong());
|
||||
messageFutures = postTargetService.sendEmbedInPostTarget(messageToSend, GiveawayPostTarget.GIVEAWAYS, serverId);
|
||||
} else {
|
||||
log.info("Sending giveaway to channel {} in server {}.", giveawayCreationRequest.getTargetChannel().getId(), serverId);
|
||||
messageFutures = channelService.sendMessageToSendToChannel(messageToSend, giveawayCreationRequest.getTargetChannel());
|
||||
}
|
||||
CompletableFutureList<Message> messageFutureList = new CompletableFutureList<>(messageFutures);
|
||||
return messageFutureList.getMainFuture().thenAccept(o -> {
|
||||
Message createdMessage = messageFutureList.getFutures().get(0).join();
|
||||
giveawayCreationRequest.setTargetChannel(createdMessage.getGuildChannel());
|
||||
self.persistGiveaway(giveawayCreationRequest, giveawayId, createdMessage.getIdLong(), componentId);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> addGiveawayParticipant(Giveaway giveaway, Member member, MessageChannel messageChannel) {
|
||||
GiveawayMessageModel giveawayMessageModel = GiveawayMessageModel.fromGiveaway(giveaway);
|
||||
giveawayMessageModel.setJoinedUserCount(giveaway.getParticipants().size() + 1L);
|
||||
Long giveawayId = giveaway.getGiveawayId().getId();
|
||||
log.info("Adding giveaway participating of user {} to giveaway {} in server {}.", member.getIdLong(), giveawayId, member.getGuild().getIdLong());
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel);
|
||||
return channelService.editEmbedMessageInAChannel(messageToSend.getEmbeds().get(0), messageChannel, giveaway.getMessageId())
|
||||
.thenAccept(message -> {
|
||||
self.persistAddedParticipant(member, giveawayId);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CompletableFuture<Void> evaluateGiveaway(Long giveawayId, Long serverId) {
|
||||
Optional<Giveaway> giveAwayOptional = giveawayManagementService.loadGiveawayById(giveawayId, serverId);
|
||||
if(giveAwayOptional.isEmpty()) {
|
||||
throw new GiveawayNotFoundException();
|
||||
}
|
||||
log.info("Evaluating giveaway {} in server {}.", giveawayId, serverId);
|
||||
Giveaway giveaway = giveAwayOptional.get();
|
||||
Set<Long> winners = new HashSet<>();
|
||||
Integer winnerCount = giveaway.getWinnerCount();
|
||||
giveaway.getParticipants().forEach(giveawayParticipant -> giveawayParticipant.setWon(false));
|
||||
List<Long> potentialWinners = new ArrayList<>(giveaway
|
||||
.getParticipants()
|
||||
.stream()
|
||||
.map(giveawayParticipant -> giveawayParticipant.getParticipant().getUserInServerId())
|
||||
.toList());
|
||||
|
||||
if(potentialWinners.size() <= winnerCount) {
|
||||
winners.addAll(potentialWinners);
|
||||
log.debug("Less participants than total winners - selecting all for giveaway {} in server {}.", giveawayId, serverId);
|
||||
} else {
|
||||
for (int i = 0; i < winnerCount; i++) {
|
||||
int winnerIndex = secureRandom.nextInt(potentialWinners.size());
|
||||
Long winner = potentialWinners.get(winnerIndex);
|
||||
potentialWinners.remove(winnerIndex);
|
||||
winners.add(winner);
|
||||
}
|
||||
}
|
||||
List<GiveawayParticipant> winningParticipants = giveaway
|
||||
.getParticipants()
|
||||
.stream()
|
||||
.filter(giveawayParticipant -> winners.contains(giveawayParticipant.getParticipant().getUserInServerId()))
|
||||
.toList();
|
||||
winningParticipants.forEach(giveawayParticipant -> giveawayParticipant.setWon(true));
|
||||
List<MemberDisplay> winnerDisplays = winningParticipants
|
||||
.stream()
|
||||
.map(giveawayParticipant -> MemberDisplay.fromAUserInAServer(giveawayParticipant.getParticipant()))
|
||||
.toList();
|
||||
GiveawayResultMessageModel resultModel = GiveawayResultMessageModel
|
||||
.builder()
|
||||
.messageId(giveaway.getMessageId())
|
||||
.title(giveaway.getTitle())
|
||||
.winners(winnerDisplays)
|
||||
.build();
|
||||
log.info("Sending result message for giveaway {} in server {}.", giveawayId, serverId);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(GIVEAWAY_RESULT_MESSAGE_TEMPLATE_KEY, resultModel);
|
||||
List<CompletableFuture<Message>> resultFutures = channelService.sendMessageEmbedToSendToAChannel(messageToSend, giveaway.getGiveawayChannel());
|
||||
|
||||
GiveawayMessageModel giveawayMessageModel = GiveawayMessageModel.fromGiveaway(giveaway);
|
||||
giveawayMessageModel.setWinners(winnerDisplays);
|
||||
giveawayMessageModel.setEnded(true);
|
||||
MessageToSend giveawayMessageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel);
|
||||
log.info("Updating original giveaway message for giveaway {} in server {}.", giveawayId, serverId);
|
||||
GuildMessageChannel messageChannel = channelService.getMessageChannelFromServer(giveaway.getServer().getId(), giveaway.getGiveawayChannel().getId());
|
||||
CompletableFuture<Message> giveawayUpdateFuture = channelService.editMessageInAChannelFuture(giveawayMessageToSend, messageChannel, giveaway.getMessageId());
|
||||
resultFutures.add(giveawayUpdateFuture);
|
||||
return new CompletableFutureList<>(resultFutures).getMainFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> cancelGiveaway(Long giveawayId, Long serverId) {
|
||||
Optional<Giveaway> giveAwayOptional = giveawayManagementService.loadGiveawayById(giveawayId, serverId);
|
||||
if(giveAwayOptional.isEmpty()) {
|
||||
throw new GiveawayNotFoundException();
|
||||
}
|
||||
Giveaway giveaway = giveAwayOptional.get();
|
||||
log.info("Cancelling giveaway with id {} in server {}.", giveawayId, serverId);
|
||||
GiveawayMessageModel giveawayMessageModel = GiveawayMessageModel.fromGiveaway(giveaway);
|
||||
giveawayMessageModel.setCancelled(true);
|
||||
schedulerService.stopTrigger(giveaway.getReminderTriggerKey());
|
||||
MessageToSend giveawayMessageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel);
|
||||
|
||||
GuildMessageChannel messageChannel = channelService.getMessageChannelFromServer(giveaway.getServer().getId(), giveaway.getGiveawayChannel().getId());
|
||||
log.debug("Updating original giveaway message to consider cancellation for giveaway {} in server {}.", giveawayId, serverId);
|
||||
return channelService.editMessageInAChannelFuture(giveawayMessageToSend, messageChannel, giveaway.getMessageId())
|
||||
.thenAccept(message -> {
|
||||
self.persistGiveawayCancellation(giveawayId, serverId);
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void persistGiveawayCancellation(Long giveawayId, Long serverId) {
|
||||
Optional<Giveaway> giveAwayOptional = giveawayManagementService.loadGiveawayById(giveawayId, serverId);
|
||||
if(giveAwayOptional.isEmpty()) {
|
||||
throw new GiveawayNotFoundException();
|
||||
}
|
||||
log.info("Persisting cancellation of giveaway {} in server {}.", giveawayId, serverId);
|
||||
Giveaway giveaway = giveAwayOptional.get();
|
||||
giveaway.setCancelled(true);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void persistAddedParticipant(Member member, Long giveawayId) {
|
||||
log.info("Storing user {} as participant to giveaway {} in server {}.", member.getIdLong(), giveawayId, member.getGuild().getIdLong());
|
||||
Optional<Giveaway> giveAwayOptional = giveawayManagementService.loadGiveawayById(giveawayId, member.getGuild().getIdLong());
|
||||
giveAwayOptional.ifPresent(giveaway -> {
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
|
||||
giveawayParticipantManagementService.addParticipant(giveaway, aUserInAServer);
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void persistGiveaway(GiveawayCreationRequest giveawayCreationRequest, Long giveawayId, Long messageId, String componentId) {
|
||||
Member creatorMember = giveawayCreationRequest.getCreator();
|
||||
log.info("Persisting giveaway in server {} with message id {}.", creatorMember.getGuild().getIdLong(), messageId);
|
||||
Instant targetDate = Instant.now().plus(giveawayCreationRequest.getDuration());
|
||||
AChannel targetChannel = channelManagementService.loadChannel(giveawayCreationRequest.getTargetChannel().getIdLong());
|
||||
AUserInAServer creator = userInServerManagementService.loadOrCreateUser(creatorMember);
|
||||
AUserInAServer benefactor;
|
||||
if(giveawayCreationRequest.getBenefactor() != null) {
|
||||
benefactor = userInServerManagementService.loadOrCreateUser(giveawayCreationRequest.getBenefactor());
|
||||
} else {
|
||||
benefactor = null;
|
||||
}
|
||||
|
||||
Giveaway giveaway = giveawayManagementService.createGiveaway(creator, benefactor, targetChannel, targetDate,
|
||||
giveawayCreationRequest.getTitle(), giveawayCreationRequest.getDescription(), giveawayCreationRequest.getWinnerCount(),
|
||||
messageId, componentId, giveawayId);
|
||||
|
||||
HashMap<Object, Object> parameters = new HashMap<>();
|
||||
parameters.put("giveawayId", giveaway.getGiveawayId().getId().toString());
|
||||
parameters.put("serverId", giveaway.getGiveawayId().getServerId().toString());
|
||||
JobParameters jobParameters = JobParameters
|
||||
.builder()
|
||||
.parameters(parameters)
|
||||
.build();
|
||||
log.info("Scheduling giveaway reminder for giveaway {} originating from message {} in server {}.", giveaway.getGiveawayId().getId(), messageId, creatorMember.getGuild().getIdLong());
|
||||
String triggerKey = schedulerService.executeJobWithParametersOnce("giveawayEvaluationJob", "giveaway", jobParameters, Date.from(giveaway.getTargetDate()));
|
||||
giveaway.setReminderTriggerKey(triggerKey);
|
||||
JoinGiveawayPayload joinPayload = JoinGiveawayPayload
|
||||
.builder()
|
||||
.giveawayId(giveaway.getGiveawayId().getId())
|
||||
.serverId(giveaway.getGiveawayId().getServerId())
|
||||
.build();
|
||||
componentPayloadService.createButtonPayload(componentId, joinPayload, GIVEAWAY_JOIN_ORIGIN, creator.getServerReference());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package dev.sheldan.abstracto.giveaway.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerSpecificId;
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
|
||||
import dev.sheldan.abstracto.giveaway.repository.GiveawayRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class GiveawayManagementServiceBean implements GiveawayManagementService {
|
||||
|
||||
@Autowired
|
||||
private GiveawayRepository giveawayRepository;
|
||||
|
||||
@Override
|
||||
public Giveaway createGiveaway(AUserInAServer creator, AUserInAServer benefactor, AChannel target,
|
||||
Instant targetDate, String title, String description, Integer winnerCount,
|
||||
Long messageId, String componentId, Long giveawayId) {
|
||||
Giveaway giveaway = Giveaway
|
||||
.builder()
|
||||
.giveawayId(new ServerSpecificId(creator.getServerReference().getId(), giveawayId))
|
||||
.creator(creator)
|
||||
.benefactor(benefactor)
|
||||
.messageId(messageId)
|
||||
.componentId(componentId)
|
||||
.server(creator.getServerReference())
|
||||
.winnerCount(winnerCount)
|
||||
.cancelled(false)
|
||||
.title(title)
|
||||
.giveawayChannel(target)
|
||||
.description(description)
|
||||
.targetDate(targetDate)
|
||||
.build();
|
||||
return giveawayRepository.save(giveaway);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Giveaway> loadGiveawayById(Long giveawayId, Long serverId) {
|
||||
return giveawayRepository.findById(new ServerSpecificId(serverId, giveawayId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package dev.sheldan.abstracto.giveaway.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.GiveawayParticipant;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.embed.GiveawayParticipationId;
|
||||
import dev.sheldan.abstracto.giveaway.repository.GiveawayParticipantRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class GiveawayParticipantManagementServiceBean implements GiveawayParticipantManagementService {
|
||||
|
||||
@Autowired
|
||||
private GiveawayParticipantRepository repository;
|
||||
|
||||
@Override
|
||||
public void addParticipant(Giveaway giveaway, AUserInAServer aUserInAServer) {
|
||||
GiveawayParticipationId id = new GiveawayParticipationId(aUserInAServer.getUserInServerId(), giveaway.getGiveawayId().getId(), giveaway.getServer().getId());
|
||||
GiveawayParticipant participant = GiveawayParticipant
|
||||
.builder()
|
||||
.id(id)
|
||||
.giveaway(giveaway)
|
||||
.participant(aUserInAServer)
|
||||
.won(false)
|
||||
.build();
|
||||
repository.save(participant);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean userIsAlreadyParticipating(Giveaway giveaway, AUserInAServer aUserInAServer) {
|
||||
return repository.existsById(new GiveawayParticipationId(aUserInAServer.getUserInServerId(), giveaway.getGiveawayId().getId(), giveaway.getServer().getId()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
abstracto.featureFlags.giveaway.featureName=giveaway
|
||||
abstracto.featureFlags.giveaway.enabled=false
|
||||
|
||||
abstracto.postTargets.giveaways.name=giveaways
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
|
||||
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<property name="utilityModule" value="(SELECT id FROM module WHERE name = 'utility')"/>
|
||||
<property name="giveawayFeature" value="(SELECT id FROM feature WHERE key = 'giveaway')"/>
|
||||
<changeSet author="Sheldan" id="giveaway-commands">
|
||||
<insert tableName="command">
|
||||
<column name="name" value="createGiveaway"/>
|
||||
<column name="module_id" valueComputed="${utilityModule}"/>
|
||||
<column name="feature_id" valueComputed="${giveawayFeature}"/>
|
||||
</insert>
|
||||
<insert tableName="command">
|
||||
<column name="name" value="reRollGiveaway"/>
|
||||
<column name="module_id" valueComputed="${utilityModule}"/>
|
||||
<column name="feature_id" valueComputed="${giveawayFeature}"/>
|
||||
</insert>
|
||||
<insert tableName="command">
|
||||
<column name="name" value="cancelGiveaway"/>
|
||||
<column name="module_id" valueComputed="${utilityModule}"/>
|
||||
<column name="feature_id" valueComputed="${giveawayFeature}"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<include file="feature.xml" relativeToChangelogFile="true"/>
|
||||
<include file="command.xml" relativeToChangelogFile="true"/>
|
||||
<include file="giveaway_evaluation_job.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<changeSet author="Sheldan" id="giveaway_feature-insertion">
|
||||
<insert tableName="feature">
|
||||
<column name="key" value="giveaway"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<changeSet author="Sheldan" id="giveaway_evaluation_job-insert">
|
||||
<insert tableName="scheduler_job">
|
||||
<column name="name" value="giveawayEvaluationJob"/>
|
||||
<column name="group_name" value="giveaway"/>
|
||||
<column name="clazz" value="dev.sheldan.abstracto.giveaway.job.GiveawayEvaluationJob"/>
|
||||
<column name="active" value="true"/>
|
||||
<column name="recovery" value="false"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<changeSet author="Sheldan" id="giveaway-table">
|
||||
<createTable tableName="giveaway">
|
||||
<column name="id" type="BIGINT">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
<column name="creator_user_id" type="INTEGER">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="benefactor_user_id" type="INTEGER">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="title" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="component_id" type="VARCHAR(100)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="description" type="VARCHAR(255)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="reminder_trigger_key" type="VARCHAR(255)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="giveaway_channel_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="message_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="cancelled" type="BOOLEAN">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="target_date" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="winner_count" type="INTEGER">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
|
||||
</createTable>
|
||||
<addPrimaryKey tableName="giveaway" columnNames="id, server_id"/>
|
||||
<addForeignKeyConstraint baseColumnNames="creator_user_id" baseTableName="giveaway" constraintName="fk_giveaway_creator" deferrable="false"
|
||||
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id"
|
||||
referencedTableName="user_in_server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="benefactor_user_id" baseTableName="giveaway" constraintName="fk_giveaway_benefactor" deferrable="false"
|
||||
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id"
|
||||
referencedTableName="user_in_server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="giveaway_channel_id" baseTableName="giveaway" constraintName="fk_giveaway_channel" deferrable="false"
|
||||
initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="channel" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="giveaway" constraintName="fk_giveaway_server" deferrable="false" initiallyDeferred="false"
|
||||
onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS giveaway_update_trigger ON giveaway;
|
||||
CREATE TRIGGER giveaway_update_trigger BEFORE UPDATE ON giveaway FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS giveaway_insert_trigger ON giveaway;
|
||||
CREATE TRIGGER giveaway_insert_trigger BEFORE INSERT ON giveaway FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||
</sql>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<changeSet author="Sheldan" id="giveaway_participant-table">
|
||||
<createTable tableName="giveaway_participant">
|
||||
<column name="participant_user_in_server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="giveaway_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="won" type="BOOLEAN">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
|
||||
</createTable>
|
||||
<addPrimaryKey columnNames="participant_user_in_server_id, giveaway_id, server_id" tableName="giveaway_participant" constraintName="pk_giveaway_participant" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="participant_user_in_server_id" baseTableName="giveaway_participant" constraintName="fk_giveaway_participant_participant"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id"
|
||||
referencedTableName="user_in_server" validate="true"/>
|
||||
<addForeignKeyConstraint baseColumnNames="giveaway_id, server_id" baseTableName="giveaway_participant" constraintName="fk_giveaway_participant_giveaway"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id, server_id"
|
||||
referencedTableName="giveaway" validate="true"/>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS giveaway_participant_update_trigger ON giveaway_participant;
|
||||
CREATE TRIGGER giveaway_participant_update_trigger BEFORE UPDATE ON giveaway_participant FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS giveaway_participant_insert_trigger ON giveaway_participant;
|
||||
CREATE TRIGGER giveaway_participant_insert_trigger BEFORE INSERT ON giveaway_participant FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<include file="giveaway.xml" relativeToChangelogFile="true"/>
|
||||
<include file="giveaway_participant.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<include file="1.5.13/collection.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>giveaway</artifactId>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>giveaway-int</artifactId>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.scheduling</groupId>
|
||||
<artifactId>scheduling-int</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,23 @@
|
||||
package dev.sheldan.abstracto.giveaway.config;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureConfig;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.config.PostTargetEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class GiveawayFeatureConfig implements FeatureConfig {
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return GiveawayFeatureDefinition.GIVEAWAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PostTargetEnum> getRequiredPostTargets() {
|
||||
return Arrays.asList(GiveawayPostTarget.GIVEAWAYS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.giveaway.config;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum GiveawayFeatureDefinition implements FeatureDefinition {
|
||||
GIVEAWAY("giveaway");
|
||||
|
||||
private String key;
|
||||
|
||||
GiveawayFeatureDefinition(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.giveaway.config;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.PostTargetEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum GiveawayPostTarget implements PostTargetEnum {
|
||||
GIVEAWAYS("giveaways");
|
||||
|
||||
private String key;
|
||||
|
||||
GiveawayPostTarget(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package dev.sheldan.abstracto.giveaway.config;
|
||||
|
||||
public class GiveawaySlashCommandNames {
|
||||
public static final String GIVEAWAY = "giveaway";
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.giveaway.exception;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
|
||||
|
||||
public class GiveawayNotFoundException extends AbstractoTemplatableException {
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return "giveaway_not_found_exception";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTemplateModel() {
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package dev.sheldan.abstracto.giveaway.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class GiveawayCreationRequest {
|
||||
|
||||
private Member creator;
|
||||
private Member benefactor;
|
||||
private String title;
|
||||
private String description;
|
||||
private Duration duration;
|
||||
@Setter
|
||||
private GuildMessageChannel targetChannel;
|
||||
private Integer winnerCount;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package dev.sheldan.abstracto.giveaway.model;
|
||||
|
||||
import dev.sheldan.abstracto.core.interaction.button.ButtonPayload;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class JoinGiveawayPayload implements ButtonPayload {
|
||||
private Long giveawayId;
|
||||
private Long serverId;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package dev.sheldan.abstracto.giveaway.model.database;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerSpecificId;
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Builder
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "giveaway")
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
public class Giveaway {
|
||||
@Id
|
||||
@EmbeddedId
|
||||
private ServerSpecificId giveawayId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "creator_user_id", nullable = false)
|
||||
private AUserInAServer creator;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "benefactor_user_id")
|
||||
private AUserInAServer benefactor;
|
||||
|
||||
@Column(name = "title", nullable = false)
|
||||
private String title;
|
||||
|
||||
@Column(name = "description")
|
||||
private String description;
|
||||
|
||||
@Column(name = "component_id")
|
||||
private String componentId;
|
||||
|
||||
@Column(name = "winner_count", nullable = false)
|
||||
private Integer winnerCount;
|
||||
|
||||
@Column(name = "target_date", nullable = false)
|
||||
private Instant targetDate;
|
||||
|
||||
@Column(name = "cancelled", nullable = false)
|
||||
private Boolean cancelled;
|
||||
|
||||
@Column(name = "reminder_trigger_key")
|
||||
private String reminderTriggerKey;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "giveaway_channel_id", nullable = false)
|
||||
private AChannel giveawayChannel;
|
||||
|
||||
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
|
||||
@MapsId("serverId")
|
||||
@JoinColumn(name = "server_id", referencedColumnName = "id", nullable = false)
|
||||
private AServer server;
|
||||
|
||||
@Column(name = "message_id", nullable = false)
|
||||
private Long messageId;
|
||||
|
||||
@Column(name = "created", nullable = false, insertable = false, updatable = false)
|
||||
private Instant created;
|
||||
|
||||
@Column(name = "updated", insertable = false, updatable = false)
|
||||
private Instant updated;
|
||||
|
||||
@OneToMany(
|
||||
fetch = FetchType.LAZY,
|
||||
orphanRemoval = true,
|
||||
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
|
||||
mappedBy = "giveaway")
|
||||
@Builder.Default
|
||||
private List<GiveawayParticipant> participants = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package dev.sheldan.abstracto.giveaway.model.database;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.embed.GiveawayParticipationId;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@Builder
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "giveaway_participant")
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
public class GiveawayParticipant {
|
||||
@EmbeddedId
|
||||
@Getter
|
||||
private GiveawayParticipationId id;
|
||||
|
||||
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
|
||||
@MapsId("participantId")
|
||||
@JoinColumn(name = "participant_user_in_server_id", nullable = false)
|
||||
private AUserInAServer participant;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumns(
|
||||
{
|
||||
@JoinColumn(updatable = false, insertable = false, name = "giveaway_id", referencedColumnName = "id"),
|
||||
@JoinColumn(updatable = false, insertable = false, name = "server_id", referencedColumnName = "server_id")
|
||||
})
|
||||
private Giveaway giveaway;
|
||||
|
||||
@Column(name = "won", nullable = false)
|
||||
private Boolean won;
|
||||
|
||||
@Column(name = "created", nullable = false, insertable = false, updatable = false)
|
||||
private Instant created;
|
||||
|
||||
@Column(name = "updated", insertable = false, updatable = false)
|
||||
private Instant updated;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.sheldan.abstracto.giveaway.model.database.embed;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import lombok.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Embeddable
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
@EqualsAndHashCode
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class GiveawayParticipationId implements Serializable {
|
||||
@Column(name = "participant_user_in_server_id")
|
||||
private Long participantId;
|
||||
|
||||
@Column(name = "giveaway_id")
|
||||
private Long giveawayId;
|
||||
|
||||
@Column(name = "server_id")
|
||||
private Long serverId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package dev.sheldan.abstracto.giveaway.model.template;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@Setter
|
||||
public class GiveawayMessageModel {
|
||||
private String title;
|
||||
private String description;
|
||||
private Integer winnerCount;
|
||||
private Long giveawayId;
|
||||
@Builder.Default
|
||||
private Boolean ended = false;
|
||||
@Builder.Default
|
||||
private Boolean cancelled = false;
|
||||
@Builder.Default
|
||||
private Long joinedUserCount = 0L;
|
||||
private MemberDisplay creator;
|
||||
private MemberDisplay benefactor;
|
||||
private Instant targetDate;
|
||||
private String joinComponentId;
|
||||
@Builder.Default
|
||||
private List<MemberDisplay> winners = new ArrayList<>();
|
||||
|
||||
public static GiveawayMessageModel fromGiveaway(Giveaway giveaway) {
|
||||
return GiveawayMessageModel
|
||||
.builder()
|
||||
.title(giveaway.getTitle())
|
||||
.description(giveaway.getDescription())
|
||||
.benefactor(giveaway.getBenefactor() != null ? MemberDisplay.fromAUserInAServer(giveaway.getBenefactor()) : null)
|
||||
.creator(MemberDisplay.fromAUserInAServer(giveaway.getCreator()))
|
||||
.winnerCount(giveaway.getWinnerCount())
|
||||
.joinedUserCount((long) giveaway.getParticipants().size())
|
||||
.joinComponentId(giveaway.getComponentId())
|
||||
.giveawayId(giveaway.getGiveawayId().getId())
|
||||
.targetDate(giveaway.getTargetDate())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.giveaway.model.template;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class GiveawayResultMessageModel {
|
||||
private String title;
|
||||
private Long messageId;
|
||||
private List<MemberDisplay> winners;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.giveaway.service;
|
||||
|
||||
import dev.sheldan.abstracto.giveaway.model.GiveawayCreationRequest;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface GiveawayService {
|
||||
CompletableFuture<Void> createGiveaway(GiveawayCreationRequest giveawayCreationRequest);
|
||||
CompletableFuture<Void> addGiveawayParticipant(Giveaway giveaway, Member member, MessageChannel messageChannel);
|
||||
CompletableFuture<Void> evaluateGiveaway(Long giveawayId, Long serverId);
|
||||
CompletableFuture<Void> cancelGiveaway(Long giveawayId, Long serverId);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.giveaway.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface GiveawayManagementService {
|
||||
Giveaway createGiveaway(AUserInAServer creator, AUserInAServer benefactor, AChannel target,
|
||||
Instant targetDate, String title, String description, Integer winnerCount, Long messageId,
|
||||
String componentId, Long giveawayId);
|
||||
Optional<Giveaway> loadGiveawayById(Long giveawayId, Long serverId);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package dev.sheldan.abstracto.giveaway.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.giveaway.model.database.Giveaway;
|
||||
|
||||
public interface GiveawayParticipantManagementService {
|
||||
void addParticipant(Giveaway giveaway, AUserInAServer aUserInAServer);
|
||||
boolean userIsAlreadyParticipating(Giveaway giveaway, AUserInAServer aUserInAServer);
|
||||
}
|
||||
25
abstracto-application/abstracto-modules/giveaway/pom.xml
Normal file
25
abstracto-application/abstracto-modules/giveaway/pom.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>giveaway</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>giveaway-int</module>
|
||||
<module>giveaway-impl</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.core</groupId>
|
||||
<artifactId>core-int</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>image-generation</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.19</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>image-generation-impl</artifactId>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/liquibase.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>image-generation-int</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,18 @@
|
||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
|
||||
<id>liquibase</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
<directory>${project.basedir}/src/main/resources/migrations</directory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
@@ -0,0 +1,147 @@
|
||||
package dev.sheldan.abstracto.imagegeneration.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.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.templating.model.AttachedFile;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FileService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationFeatureDefinition;
|
||||
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationSlashCommandNames;
|
||||
import dev.sheldan.abstracto.imagegeneration.service.ImageGenerationService;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class Bonk extends AbstractConditionableCommand {
|
||||
public static final String MEMBER_PARAMETER_KEY = "member";
|
||||
|
||||
@Autowired
|
||||
private ImageGenerationService imageGenerationService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Autowired
|
||||
private FileService fileService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
private static final String BONK_EMBED_TEMPLATE_KEY = "bonk_response";
|
||||
|
||||
@Value("${abstracto.feature.imagegeneration.bonk.imagesize}")
|
||||
private Integer imageSize;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
Member member;
|
||||
List<Object> parameters = commandContext.getParameters().getParameters();
|
||||
if(parameters.isEmpty()) {
|
||||
member = commandContext.getAuthor();
|
||||
} else {
|
||||
member = (Member) parameters.get(0);
|
||||
}
|
||||
File bonkGifFile = imageGenerationService.getBonkGif(member.getEffectiveAvatar().getUrl(imageSize));
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(BONK_EMBED_TEMPLATE_KEY, new Object());
|
||||
// template support does not support binary files
|
||||
AttachedFile file = AttachedFile
|
||||
.builder()
|
||||
.file(bonkGifFile)
|
||||
.fileName("bonk.gif")
|
||||
.build();
|
||||
messageToSend.getAttachedFiles().add(file);
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenAccept(unused -> fileService.safeDeleteIgnoreException(messageToSend.getAttachedFiles().get(0).getFile()))
|
||||
.thenApply(unused -> CommandResult.fromIgnored());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
event.deferReply().queue();
|
||||
Member targetMember;
|
||||
if(slashCommandParameterService.hasCommandOption(MEMBER_PARAMETER_KEY, event)) {
|
||||
targetMember = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER_KEY, event, Member.class);
|
||||
} else {
|
||||
targetMember = event.getMember();
|
||||
}
|
||||
File bonkGifFile = imageGenerationService.getBonkGif(targetMember.getEffectiveAvatar().getUrl(imageSize));
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(BONK_EMBED_TEMPLATE_KEY, new Object());
|
||||
// template support does not support binary files
|
||||
AttachedFile file = AttachedFile
|
||||
.builder()
|
||||
.file(bonkGifFile)
|
||||
.fileName("bonk.gif")
|
||||
.build();
|
||||
messageToSend.getAttachedFiles().add(file);
|
||||
return FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(messageToSend, event.getHook()))
|
||||
.thenAccept(unused -> fileService.safeDeleteIgnoreException(messageToSend.getAttachedFiles().get(0).getFile()))
|
||||
.thenApply(unused -> CommandResult.fromIgnored());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
Parameter memberParameter = Parameter
|
||||
.builder()
|
||||
.name(MEMBER_PARAMETER_KEY)
|
||||
.type(Member.class)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
parameters.add(memberParameter);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(ImageGenerationSlashCommandNames.IMAGE_GENERATION)
|
||||
.groupName("memes")
|
||||
.commandName("bonk")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name("bonk")
|
||||
.module(UtilityModuleDefinition.UTILITY)
|
||||
.templated(true)
|
||||
.supportsEmbedException(true)
|
||||
.async(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return ImageGenerationFeatureDefinition.IMAGE_GENERATION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package dev.sheldan.abstracto.imagegeneration.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.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.templating.model.AttachedFile;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FileService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationFeatureDefinition;
|
||||
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationSlashCommandNames;
|
||||
import dev.sheldan.abstracto.imagegeneration.service.ImageGenerationService;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class Pat extends AbstractConditionableCommand {
|
||||
public static final String MEMBER_PARAMETER_KEY = "member";
|
||||
|
||||
@Autowired
|
||||
private ImageGenerationService imageGenerationService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Autowired
|
||||
private FileService fileService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
private static final String PAT_EMBED_TEMPLATE_KEY = "pat_response";
|
||||
|
||||
@Value("${abstracto.feature.imagegeneration.pat.imagesize}")
|
||||
private Integer imageSize;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
Member member;
|
||||
List<Object> parameters = commandContext.getParameters().getParameters();
|
||||
if(parameters.isEmpty()) {
|
||||
member = commandContext.getAuthor();
|
||||
} else {
|
||||
member = (Member) parameters.get(0);
|
||||
}
|
||||
File patGifFile = imageGenerationService.getPatGif(member.getEffectiveAvatar().getUrl(imageSize));
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(PAT_EMBED_TEMPLATE_KEY, new Object());
|
||||
// template support does not support binary files
|
||||
AttachedFile file = AttachedFile
|
||||
.builder()
|
||||
.file(patGifFile)
|
||||
.fileName("pat.gif")
|
||||
.build();
|
||||
messageToSend.getAttachedFiles().add(file);
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenAccept(unused -> fileService.safeDeleteIgnoreException(messageToSend.getAttachedFiles().get(0).getFile()))
|
||||
.thenApply(unused -> CommandResult.fromIgnored());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
event.deferReply().queue();
|
||||
Member targetMember;
|
||||
if(slashCommandParameterService.hasCommandOption(MEMBER_PARAMETER_KEY, event)) {
|
||||
targetMember = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER_KEY, event, Member.class);
|
||||
} else {
|
||||
targetMember = event.getMember();
|
||||
}
|
||||
File patGifFile = imageGenerationService.getPatGif(targetMember.getEffectiveAvatar().getUrl(imageSize));
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(PAT_EMBED_TEMPLATE_KEY, new Object());
|
||||
// template support does not support binary files
|
||||
AttachedFile file = AttachedFile
|
||||
.builder()
|
||||
.file(patGifFile)
|
||||
.fileName("pat.gif")
|
||||
.build();
|
||||
messageToSend.getAttachedFiles().add(file);
|
||||
return FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(messageToSend, event.getHook()))
|
||||
.thenAccept(unused -> fileService.safeDeleteIgnoreException(messageToSend.getAttachedFiles().get(0).getFile()))
|
||||
.thenApply(unused -> CommandResult.fromIgnored());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
Parameter memberParameter = Parameter
|
||||
.builder()
|
||||
.name(MEMBER_PARAMETER_KEY)
|
||||
.type(Member.class)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
parameters.add(memberParameter);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(ImageGenerationSlashCommandNames.IMAGE_GENERATION)
|
||||
.groupName("memes")
|
||||
.commandName("pat")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name("pat")
|
||||
.module(UtilityModuleDefinition.UTILITY)
|
||||
.templated(true)
|
||||
.supportsEmbedException(true)
|
||||
.async(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return ImageGenerationFeatureDefinition.IMAGE_GENERATION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package dev.sheldan.abstracto.imagegeneration.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.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.templating.model.AttachedFile;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FileService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationFeatureDefinition;
|
||||
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationSlashCommandNames;
|
||||
import dev.sheldan.abstracto.imagegeneration.service.ImageGenerationService;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class Triggered extends AbstractConditionableCommand {
|
||||
|
||||
public static final String MEMBER_PARAMETER_KEY = "member";
|
||||
@Autowired
|
||||
private ImageGenerationService imageGenerationService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Autowired
|
||||
private FileService fileService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
private static final String TRIGGERED_EMBED_TEMPLATE_KEY = "triggered_response";
|
||||
|
||||
@Value("${abstracto.feature.imagegeneration.triggered.imagesize}")
|
||||
private Integer imageSize;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
Member member;
|
||||
List<Object> parameters = commandContext.getParameters().getParameters();
|
||||
if(parameters.isEmpty()) {
|
||||
member = commandContext.getAuthor();
|
||||
} else {
|
||||
member = (Member) parameters.get(0);
|
||||
}
|
||||
File triggeredGifFile = imageGenerationService.getTriggeredGif(member.getEffectiveAvatar().getUrl(imageSize));
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(TRIGGERED_EMBED_TEMPLATE_KEY, new Object());
|
||||
// template support does not support binary files
|
||||
AttachedFile file = AttachedFile
|
||||
.builder()
|
||||
.file(triggeredGifFile)
|
||||
.fileName("triggered.gif")
|
||||
.build();
|
||||
messageToSend.getAttachedFiles().add(file);
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenAccept(unused -> fileService.safeDeleteIgnoreException(messageToSend.getAttachedFiles().get(0).getFile()))
|
||||
.thenApply(unused -> CommandResult.fromIgnored());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
event.deferReply().queue();
|
||||
Member targetMember;
|
||||
if(slashCommandParameterService.hasCommandOption(MEMBER_PARAMETER_KEY, event)) {
|
||||
targetMember = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER_KEY, event, Member.class);
|
||||
} else {
|
||||
targetMember = event.getMember();
|
||||
}
|
||||
File triggeredGifFile = imageGenerationService.getTriggeredGif(targetMember.getEffectiveAvatar().getUrl(imageSize));
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(TRIGGERED_EMBED_TEMPLATE_KEY, new Object());
|
||||
// template support does not support binary files
|
||||
AttachedFile file = AttachedFile
|
||||
.builder()
|
||||
.file(triggeredGifFile)
|
||||
.fileName("triggered.gif")
|
||||
.build();
|
||||
messageToSend.getAttachedFiles().add(file);
|
||||
return FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(messageToSend, event.getHook()))
|
||||
.thenAccept(unused -> fileService.safeDeleteIgnoreException(messageToSend.getAttachedFiles().get(0).getFile()))
|
||||
.thenApply(unused -> CommandResult.fromIgnored());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
Parameter memberParameter = Parameter
|
||||
.builder()
|
||||
.name(MEMBER_PARAMETER_KEY)
|
||||
.type(Member.class)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
parameters.add(memberParameter);
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(ImageGenerationSlashCommandNames.IMAGE_GENERATION)
|
||||
.groupName("memes")
|
||||
.commandName("triggered")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name("triggered")
|
||||
.module(UtilityModuleDefinition.UTILITY)
|
||||
.templated(true)
|
||||
.supportsEmbedException(true)
|
||||
.async(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return ImageGenerationFeatureDefinition.IMAGE_GENERATION;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.sheldan.abstracto.imagegeneration.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
@Configuration
|
||||
@PropertySource("classpath:image-generation-config.properties")
|
||||
public class ImageGenerationConfig {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package dev.sheldan.abstracto.imagegeneration.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.service.HttpService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
@Component
|
||||
public class ImageGenerationServiceBean implements ImageGenerationService {
|
||||
|
||||
@Value("${abstracto.feature.imagegeneration.triggered.url}")
|
||||
private String triggeredUrl;
|
||||
|
||||
@Value("${abstracto.feature.imagegeneration.pat.url}")
|
||||
private String patUrl;
|
||||
|
||||
@Value("${abstracto.feature.imagegeneration.bonk.url}")
|
||||
private String bonkUrl;
|
||||
|
||||
@Autowired
|
||||
private HttpService httpService;
|
||||
|
||||
@Override
|
||||
public File getTriggeredGif(String imageUrl) {
|
||||
try {
|
||||
return httpService.downloadFileToTempFile(triggeredUrl.replace("{1}", imageUrl));
|
||||
} catch (IOException e) {
|
||||
throw new AbstractoRunTimeException(String.format("Failed to download triggered gif for url %s with error %s", imageUrl, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getPatGif(String imageUrl) {
|
||||
try {
|
||||
return httpService.downloadFileToTempFile(patUrl.replace("{1}", imageUrl));
|
||||
} catch (IOException e) {
|
||||
throw new AbstractoRunTimeException(String.format("Failed to download pat gif for url %s with error %s", imageUrl, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getBonkGif(String imageUrl) {
|
||||
try {
|
||||
return httpService.downloadFileToTempFile(bonkUrl.replace("{1}", imageUrl));
|
||||
} catch (IOException e) {
|
||||
throw new AbstractoRunTimeException(String.format("Failed to download bonk gif for url %s with error %s", imageUrl, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
abstracto.featureFlags.imageGeneration.featureName=imageGeneration
|
||||
abstracto.featureFlags.imageGeneration.enabled=false
|
||||
|
||||
abstracto.feature.imagegeneration.triggered.url=http://${PRIVATE_REST_API_HOST}:${PRIVATE_REST_API_PORT}/memes/triggered/file.gif?url={1}
|
||||
abstracto.feature.imagegeneration.triggered.imagesize=128
|
||||
|
||||
abstracto.feature.imagegeneration.pat.url=http://${PRIVATE_REST_API_HOST}:${PRIVATE_REST_API_PORT}/memes/pat/file.gif?url={1}
|
||||
abstracto.feature.imagegeneration.pat.imagesize=128
|
||||
|
||||
abstracto.feature.imagegeneration.bonk.url=http://${PRIVATE_REST_API_HOST}:${PRIVATE_REST_API_PORT}/memes/bonk/file.gif?url={1}
|
||||
abstracto.feature.imagegeneration.bonk.imagesize=128
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user