mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-28 08:38:52 +00:00
[AB-268] adding button feature mode to suggestions which allows for hidden suggestion votes
moving gateway metric to separate service in case JDA is not ready yet
This commit is contained in:
@@ -28,7 +28,7 @@ public class TrackedEmote implements Serializable, Fakeable {
|
|||||||
@EmbeddedId
|
@EmbeddedId
|
||||||
private ServerSpecificId trackedEmoteId;
|
private ServerSpecificId trackedEmoteId;
|
||||||
|
|
||||||
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
|
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
|
||||||
@MapsId("serverId")
|
@MapsId("serverId")
|
||||||
@JoinColumn(name = "server_id", referencedColumnName = "id", nullable = false)
|
@JoinColumn(name = "server_id", referencedColumnName = "id", nullable = false)
|
||||||
private AServer server;
|
private AServer server;
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.command;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
|
||||||
|
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||||
|
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||||
|
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||||
|
import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||||
|
import dev.sheldan.abstracto.core.command.config.ParameterValidator;
|
||||||
|
import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator;
|
||||||
|
import dev.sheldan.abstracto.core.command.execution.CommandContext;
|
||||||
|
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||||
|
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||||
|
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||||
|
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||||
|
import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureDefinition;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.template.SuggestionInfoModel;
|
||||||
|
import dev.sheldan.abstracto.suggestion.service.SuggestionService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ShowSuggestion extends AbstractConditionableCommand {
|
||||||
|
|
||||||
|
public static final String SHOW_SUGGESTION_TEMPLATE_KEY = "suggestion_info_response";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SuggestionService suggestionService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ChannelService channelService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||||
|
List<Object> parameters = commandContext.getParameters().getParameters();
|
||||||
|
Long suggestionId = (Long) parameters.get(0);
|
||||||
|
|
||||||
|
SuggestionInfoModel suggestionInfoModel = suggestionService.getSuggestionInfo(commandContext.getGuild().getIdLong(), suggestionId);
|
||||||
|
return FutureUtils.toSingleFutureGeneric(
|
||||||
|
channelService.sendEmbedTemplateInTextChannelList(SHOW_SUGGESTION_TEMPLATE_KEY, suggestionInfoModel, commandContext.getChannel()))
|
||||||
|
.thenApply(unused -> CommandResult.fromSuccess());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandConfiguration getConfiguration() {
|
||||||
|
List<Parameter> parameters = new ArrayList<>();
|
||||||
|
|
||||||
|
List<ParameterValidator> suggestionIdValidator = Arrays.asList(MinIntegerValueValidator.min(1L));
|
||||||
|
parameters.add(Parameter.builder().name("suggestionId").validators(suggestionIdValidator).type(Long.class).templated(true).build());
|
||||||
|
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(false).build();
|
||||||
|
return CommandConfiguration.builder()
|
||||||
|
.name("showSuggestion")
|
||||||
|
.module(UtilityModuleDefinition.UTILITY)
|
||||||
|
.templated(true)
|
||||||
|
.async(true)
|
||||||
|
.supportsEmbedException(true)
|
||||||
|
.causesReaction(true)
|
||||||
|
.parameters(parameters)
|
||||||
|
.help(helpInfo)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureDefinition getFeature() {
|
||||||
|
return SuggestionFeatureDefinition.SUGGEST;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.listener;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||||
|
import dev.sheldan.abstracto.core.config.ListenerPriority;
|
||||||
|
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||||
|
import dev.sheldan.abstracto.core.listener.ButtonClickedListenerResult;
|
||||||
|
import dev.sheldan.abstracto.core.listener.async.jda.ButtonClickedListener;
|
||||||
|
import dev.sheldan.abstracto.core.models.listener.ButtonClickedListenerModel;
|
||||||
|
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||||
|
import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureDefinition;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.template.SuggestionButtonPayload;
|
||||||
|
import dev.sheldan.abstracto.suggestion.service.SuggestionServiceBean;
|
||||||
|
import dev.sheldan.abstracto.suggestion.service.SuggestionVoteService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.ButtonClickEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.ButtonInteraction;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class SuggestionButtonVoteClickedListener implements ButtonClickedListener {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SuggestionVoteService suggestionVoteService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private InteractionService interactionService;
|
||||||
|
|
||||||
|
public static final String VOTE_REMOVED_TEMPLATE_KEY = "suggestion_vote_removed_notification";
|
||||||
|
public static final String VOTE_CAST_TEMPLATE_KEY = "suggestion_vote_cast_notification";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
|
||||||
|
ButtonClickEvent event = model.getEvent();
|
||||||
|
SuggestionButtonPayload payload = (SuggestionButtonPayload) model.getDeserializedPayload();
|
||||||
|
suggestionVoteService.upsertSuggestionVote(event.getMember(), payload.getDecision(), payload.getSuggestionId());
|
||||||
|
ButtonInteraction buttonInteraction = model.getEvent().getInteraction();
|
||||||
|
String templateToUse;
|
||||||
|
switch (payload.getDecision()) {
|
||||||
|
case AGREE:
|
||||||
|
case DISAGREE:
|
||||||
|
templateToUse = VOTE_CAST_TEMPLATE_KEY;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case REMOVE_VOTE:
|
||||||
|
templateToUse = VOTE_REMOVED_TEMPLATE_KEY;
|
||||||
|
}
|
||||||
|
FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(templateToUse, new Object(), buttonInteraction.getHook()))
|
||||||
|
.thenAccept(unused -> log.info("Notified user {} about vote action in suggestion {} in server {}.",
|
||||||
|
model.getEvent().getMember().getIdLong(), payload.getSuggestionId(), payload.getServerId()));
|
||||||
|
|
||||||
|
return ButtonClickedListenerResult.ACKNOWLEDGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean handlesEvent(ButtonClickedListenerModel model) {
|
||||||
|
return model.getOrigin().equals(SuggestionServiceBean.SUGGESTION_VOTE_ORIGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureDefinition getFeature() {
|
||||||
|
return SuggestionFeatureDefinition.SUGGEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getPriority() {
|
||||||
|
return ListenerPriority.MEDIUM;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,9 +8,12 @@ import org.springframework.stereotype.Repository;
|
|||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface SuggestionRepository extends JpaRepository<Suggestion, ServerSpecificId> {
|
public interface SuggestionRepository extends JpaRepository<Suggestion, ServerSpecificId> {
|
||||||
List<Suggestion> findByUpdatedLessThanAndStateNot(Instant start, SuggestionState state);
|
List<Suggestion> findByUpdatedLessThanAndStateNot(Instant start, SuggestionState state);
|
||||||
|
|
||||||
|
Optional<Suggestion> findByMessageId(Long messageId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.repository;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionDecision;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionVote;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.embed.SuggestionVoterId;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface SuggestionVoteRepository extends JpaRepository<SuggestionVote, SuggestionVoterId> {
|
||||||
|
Long countByDecisionAndSuggestionVoteId_SuggestionIdAndSuggestionVoteId_ServerId(SuggestionDecision decision, Long suggestionId, Long serverId);
|
||||||
|
void deleteBySuggestionVoteId_SuggestionIdAndSuggestionVoteId_ServerId(Long suggestionId, Long serverId);
|
||||||
|
}
|
||||||
@@ -5,7 +5,9 @@ import dev.sheldan.abstracto.core.models.ServerSpecificId;
|
|||||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.core.models.template.button.ButtonConfigModel;
|
||||||
import dev.sheldan.abstracto.core.service.*;
|
import dev.sheldan.abstracto.core.service.*;
|
||||||
|
import dev.sheldan.abstracto.core.service.management.ComponentPayloadManagementService;
|
||||||
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
|
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
|
||||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||||
@@ -18,10 +20,11 @@ import dev.sheldan.abstracto.suggestion.config.SuggestionFeatureMode;
|
|||||||
import dev.sheldan.abstracto.suggestion.config.SuggestionPostTarget;
|
import dev.sheldan.abstracto.suggestion.config.SuggestionPostTarget;
|
||||||
import dev.sheldan.abstracto.suggestion.exception.UnSuggestNotPossibleException;
|
import dev.sheldan.abstracto.suggestion.exception.UnSuggestNotPossibleException;
|
||||||
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
|
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionDecision;
|
||||||
import dev.sheldan.abstracto.suggestion.model.database.SuggestionState;
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionState;
|
||||||
import dev.sheldan.abstracto.suggestion.model.template.SuggestionLog;
|
import dev.sheldan.abstracto.suggestion.model.template.*;
|
||||||
import dev.sheldan.abstracto.suggestion.model.template.SuggestionReminderModel;
|
|
||||||
import dev.sheldan.abstracto.suggestion.service.management.SuggestionManagementService;
|
import dev.sheldan.abstracto.suggestion.service.management.SuggestionManagementService;
|
||||||
|
import dev.sheldan.abstracto.suggestion.service.management.SuggestionVoteManagementService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.dv8tion.jda.api.entities.*;
|
import net.dv8tion.jda.api.entities.*;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -47,6 +50,7 @@ public class SuggestionServiceBean implements SuggestionService {
|
|||||||
public static final String SUGGESTION_NO_EMOTE = "suggestionNo";
|
public static final String SUGGESTION_NO_EMOTE = "suggestionNo";
|
||||||
public static final String SUGGESTION_COUNTER_KEY = "suggestion";
|
public static final String SUGGESTION_COUNTER_KEY = "suggestion";
|
||||||
public static final String SUGGESTION_REMINDER_TEMPLATE_KEY = "suggest_suggestion_reminder";
|
public static final String SUGGESTION_REMINDER_TEMPLATE_KEY = "suggest_suggestion_reminder";
|
||||||
|
public static final String SUGGESTION_VOTE_ORIGIN = "suggestionVote";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SuggestionManagementService suggestionManagementService;
|
private SuggestionManagementService suggestionManagementService;
|
||||||
@@ -93,6 +97,15 @@ public class SuggestionServiceBean implements SuggestionService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ConfigService configService;
|
private ConfigService configService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ComponentService componentService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ComponentPayloadManagementService componentPayloadManagementService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SuggestionVoteManagementService suggestionVoteManagementService;
|
||||||
|
|
||||||
@Value("${abstracto.feature.suggestion.removalMaxAge}")
|
@Value("${abstracto.feature.suggestion.removalMaxAge}")
|
||||||
private Long removalMaxAgeSeconds;
|
private Long removalMaxAgeSeconds;
|
||||||
|
|
||||||
@@ -106,6 +119,7 @@ public class SuggestionServiceBean implements SuggestionService {
|
|||||||
AServer server = serverManagementService.loadServer(serverId);
|
AServer server = serverManagementService.loadServer(serverId);
|
||||||
AUserInAServer userSuggester = userInServerManagementService.loadOrCreateUser(suggester);
|
AUserInAServer userSuggester = userInServerManagementService.loadOrCreateUser(suggester);
|
||||||
Long newSuggestionId = counterService.getNextCounterValue(server, SUGGESTION_COUNTER_KEY);
|
Long newSuggestionId = counterService.getNextCounterValue(server, SUGGESTION_COUNTER_KEY);
|
||||||
|
Boolean useButtons = featureModeService.featureModeActive(SuggestionFeatureDefinition.SUGGEST, serverId, SuggestionFeatureMode.SUGGESTION_BUTTONS);
|
||||||
SuggestionLog model = SuggestionLog
|
SuggestionLog model = SuggestionLog
|
||||||
.builder()
|
.builder()
|
||||||
.suggestionId(newSuggestionId)
|
.suggestionId(newSuggestionId)
|
||||||
@@ -114,14 +128,35 @@ public class SuggestionServiceBean implements SuggestionService {
|
|||||||
.message(commandMessage)
|
.message(commandMessage)
|
||||||
.member(commandMessage.getMember())
|
.member(commandMessage.getMember())
|
||||||
.suggesterUser(userSuggester)
|
.suggesterUser(userSuggester)
|
||||||
|
.useButtons(useButtons)
|
||||||
.suggester(suggester.getUser())
|
.suggester(suggester.getUser())
|
||||||
.text(text)
|
.text(text)
|
||||||
.build();
|
.build();
|
||||||
|
if(useButtons) {
|
||||||
|
setupButtonIds(model);
|
||||||
|
}
|
||||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SUGGESTION_CREATION_TEMPLATE, model, serverId);
|
MessageToSend messageToSend = templateService.renderEmbedTemplate(SUGGESTION_CREATION_TEMPLATE, model, serverId);
|
||||||
log.info("Creating suggestion with id {} in server {} from member {}.", newSuggestionId, serverId, suggester.getIdLong());
|
log.info("Creating suggestion with id {} in server {} from member {}.", newSuggestionId, serverId, suggester.getIdLong());
|
||||||
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, SuggestionPostTarget.SUGGESTION, serverId);
|
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, SuggestionPostTarget.SUGGESTION, serverId);
|
||||||
return FutureUtils.toSingleFutureGeneric(completableFutures).thenCompose(aVoid -> {
|
return FutureUtils.toSingleFutureGeneric(completableFutures)
|
||||||
Message message = completableFutures.get(0).join();
|
.thenCompose(aVoid -> self.addDeletionPossibility(commandMessage, text, suggester, serverId, newSuggestionId, completableFutures, model));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public CompletableFuture<Void> addDeletionPossibility(Message commandMessage, String text, Member suggester, Long serverId,
|
||||||
|
Long newSuggestionId, List<CompletableFuture<Message>> completableFutures, SuggestionLog model) {
|
||||||
|
Message message = completableFutures.get(0).join();
|
||||||
|
if(model.getUseButtons()) {
|
||||||
|
configureDecisionButtonPayload(serverId, newSuggestionId, model.getAgreeButtonModel(), SuggestionDecision.AGREE);
|
||||||
|
configureDecisionButtonPayload(serverId, newSuggestionId, model.getDisAgreeButtonModel(), SuggestionDecision.DISAGREE);
|
||||||
|
configureDecisionButtonPayload(serverId, newSuggestionId, model.getRemoveVoteButtonModel(), SuggestionDecision.REMOVE_VOTE);
|
||||||
|
AServer server = serverManagementService.loadServer(serverId);
|
||||||
|
componentPayloadManagementService.createPayload(model.getAgreeButtonModel(), server);
|
||||||
|
componentPayloadManagementService.createPayload(model.getDisAgreeButtonModel(), server);
|
||||||
|
componentPayloadManagementService.createPayload(model.getRemoveVoteButtonModel(), server);
|
||||||
|
self.persistSuggestionInDatabase(suggester, text, message, newSuggestionId, commandMessage);
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
} else {
|
||||||
log.debug("Posted message, adding reaction for suggestion {} to message {}.", newSuggestionId, message.getId());
|
log.debug("Posted message, adding reaction for suggestion {} to message {}.", newSuggestionId, message.getId());
|
||||||
CompletableFuture<Void> firstReaction = reactionService.addReactionToMessageAsync(SUGGESTION_YES_EMOTE, serverId, message);
|
CompletableFuture<Void> firstReaction = reactionService.addReactionToMessageAsync(SUGGESTION_YES_EMOTE, serverId, message);
|
||||||
CompletableFuture<Void> secondReaction = reactionService.addReactionToMessageAsync(SUGGESTION_NO_EMOTE, serverId, message);
|
CompletableFuture<Void> secondReaction = reactionService.addReactionToMessageAsync(SUGGESTION_NO_EMOTE, serverId, message);
|
||||||
@@ -129,7 +164,25 @@ public class SuggestionServiceBean implements SuggestionService {
|
|||||||
log.debug("Reaction added to message {} for suggestion {}.", message.getId(), newSuggestionId);
|
log.debug("Reaction added to message {} for suggestion {}.", message.getId(), newSuggestionId);
|
||||||
self.persistSuggestionInDatabase(suggester, text, message, newSuggestionId, commandMessage);
|
self.persistSuggestionInDatabase(suggester, text, message, newSuggestionId, commandMessage);
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void configureDecisionButtonPayload(Long serverId, Long newSuggestionId, ButtonConfigModel model, SuggestionDecision decision) {
|
||||||
|
SuggestionButtonPayload agreePayload = SuggestionButtonPayload
|
||||||
|
.builder()
|
||||||
|
.suggestionId(newSuggestionId)
|
||||||
|
.serverId(serverId)
|
||||||
|
.decision(decision)
|
||||||
|
.build();
|
||||||
|
model.setButtonPayload(agreePayload);
|
||||||
|
model.setOrigin(SUGGESTION_VOTE_ORIGIN);
|
||||||
|
model.setPayloadType(SuggestionButtonPayload.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupButtonIds(SuggestionLog suggestionLog) {
|
||||||
|
suggestionLog.setAgreeButtonModel(componentService.createButtonConfigModel());
|
||||||
|
suggestionLog.setDisAgreeButtonModel(componentService.createButtonConfigModel());
|
||||||
|
suggestionLog.setRemoveVoteButtonModel(componentService.createButtonConfigModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -179,26 +232,32 @@ public class SuggestionServiceBean implements SuggestionService {
|
|||||||
Long serverId = suggestion.getServer().getId();
|
Long serverId = suggestion.getServer().getId();
|
||||||
Long channelId = suggestion.getChannel().getId();
|
Long channelId = suggestion.getChannel().getId();
|
||||||
Long originalMessageId = suggestion.getMessageId();
|
Long originalMessageId = suggestion.getMessageId();
|
||||||
SuggestionLog model = SuggestionLog
|
Long agreements = suggestionVoteManagementService.getDecisionsForSuggestion(suggestion, SuggestionDecision.AGREE);
|
||||||
|
Long disagreements = suggestionVoteManagementService.getDecisionsForSuggestion(suggestion, SuggestionDecision.DISAGREE);
|
||||||
|
Long suggestionId = suggestion.getSuggestionId().getId();
|
||||||
|
SuggestionUpdateModel model = SuggestionUpdateModel
|
||||||
.builder()
|
.builder()
|
||||||
.suggestionId(suggestion.getSuggestionId().getId())
|
.suggestionId(suggestionId)
|
||||||
.state(suggestion.getState())
|
.state(suggestion.getState())
|
||||||
.suggesterUser(suggestion.getSuggester())
|
|
||||||
.serverId(serverId)
|
.serverId(serverId)
|
||||||
.member(memberExecutingCommand)
|
.member(memberExecutingCommand)
|
||||||
|
.agreeVotes(agreements)
|
||||||
|
.disAgreeVotes(disagreements)
|
||||||
.originalMessageId(originalMessageId)
|
.originalMessageId(originalMessageId)
|
||||||
.text(suggestion.getSuggestionText())
|
.text(suggestion.getSuggestionText())
|
||||||
.originalChannelId(channelId)
|
.originalChannelId(channelId)
|
||||||
.reason(reason)
|
.reason(reason)
|
||||||
.build();
|
.build();
|
||||||
log.info("Updated posted suggestion {} in server {}.", suggestion.getSuggestionId().getId(), suggestion.getServer().getId());
|
log.info("Updated posted suggestion {} in server {}.", suggestionId, suggestion.getServer().getId());
|
||||||
CompletableFuture<User> memberById = userService.retrieveUserForId(suggestion.getSuggester().getUserReference().getId());
|
CompletableFuture<User> memberById = userService.retrieveUserForId(suggestion.getSuggester().getUserReference().getId());
|
||||||
CompletableFuture<Void> finalFuture = new CompletableFuture<>();
|
CompletableFuture<Void> finalFuture = new CompletableFuture<>();
|
||||||
memberById.whenComplete((user, throwable) -> {
|
memberById.whenComplete((user, throwable) -> {
|
||||||
if(throwable == null) {
|
if(throwable == null) {
|
||||||
model.setSuggester(user);
|
model.setSuggester(user);
|
||||||
}
|
}
|
||||||
self.updateSuggestionMessageText(reason, model).thenAccept(unused -> finalFuture.complete(null)).exceptionally(throwable1 -> {
|
self.updateSuggestionMessageText(reason, model).thenAccept(unused -> finalFuture.complete(null))
|
||||||
|
.thenAccept(unused -> self.removeSuggestionButtons(serverId, channelId, originalMessageId, suggestionId))
|
||||||
|
.exceptionally(throwable1 -> {
|
||||||
finalFuture.completeExceptionally(throwable1);
|
finalFuture.completeExceptionally(throwable1);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@@ -208,11 +267,23 @@ public class SuggestionServiceBean implements SuggestionService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return finalFuture;
|
return finalFuture;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public CompletableFuture<Void> updateSuggestionMessageText(String text, SuggestionLog suggestionLog) {
|
public CompletableFuture<Void> removeSuggestionButtons(Long serverId, Long channelId, Long messageId, Long suggestionId) {
|
||||||
|
Boolean useButtons = featureModeService.featureModeActive(SuggestionFeatureDefinition.SUGGEST, serverId, SuggestionFeatureMode.SUGGESTION_BUTTONS);
|
||||||
|
if(useButtons) {
|
||||||
|
return messageService.loadMessage(serverId, channelId, messageId).thenCompose(message -> {
|
||||||
|
log.info("Clearing buttons from suggestion {} in with message {} in channel {} in server {}.", suggestionId, message, channelId, serverId);
|
||||||
|
return componentService.clearButtons(message);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public CompletableFuture<Void> updateSuggestionMessageText(String text, SuggestionUpdateModel suggestionLog) {
|
||||||
suggestionLog.setReason(text);
|
suggestionLog.setReason(text);
|
||||||
Long serverId = suggestionLog.getServerId();
|
Long serverId = suggestionLog.getServerId();
|
||||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SUGGESTION_UPDATE_TEMPLATE, suggestionLog, serverId);
|
MessageToSend messageToSend = templateService.renderEmbedTemplate(SUGGESTION_UPDATE_TEMPLATE, suggestionLog, serverId);
|
||||||
@@ -253,8 +324,11 @@ public class SuggestionServiceBean implements SuggestionService {
|
|||||||
Instant pointInTime = Instant.now().minus(Duration.ofDays(autoRemovalMaxDays)).truncatedTo(ChronoUnit.DAYS);
|
Instant pointInTime = Instant.now().minus(Duration.ofDays(autoRemovalMaxDays)).truncatedTo(ChronoUnit.DAYS);
|
||||||
List<Suggestion> suggestionsToRemove = suggestionManagementService.getSuggestionsUpdatedBeforeNotNew(pointInTime);
|
List<Suggestion> suggestionsToRemove = suggestionManagementService.getSuggestionsUpdatedBeforeNotNew(pointInTime);
|
||||||
log.info("Removing {} suggestions older than {}.", suggestionsToRemove.size(), pointInTime);
|
log.info("Removing {} suggestions older than {}.", suggestionsToRemove.size(), pointInTime);
|
||||||
suggestionsToRemove.forEach(suggestion -> log.info("Deleting suggestion {} in server {}.",
|
suggestionsToRemove.forEach(suggestion -> {
|
||||||
suggestion.getSuggestionId().getId(), suggestion.getSuggestionId().getServerId()));
|
suggestionVoteManagementService.deleteSuggestionVotes(suggestion);
|
||||||
|
log.info("Deleting suggestion {} in server {}.",
|
||||||
|
suggestion.getSuggestionId().getId(), suggestion.getSuggestionId().getServerId());
|
||||||
|
});
|
||||||
suggestionManagementService.deleteSuggestion(suggestionsToRemove);
|
suggestionManagementService.deleteSuggestion(suggestionsToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,6 +372,18 @@ public class SuggestionServiceBean implements SuggestionService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SuggestionInfoModel getSuggestionInfo(Long serverId, Long suggestionId) {
|
||||||
|
Suggestion suggestion = suggestionManagementService.getSuggestion(serverId, suggestionId);
|
||||||
|
Long agreements = suggestionVoteManagementService.getDecisionsForSuggestion(suggestion, SuggestionDecision.AGREE);
|
||||||
|
Long disagreements = suggestionVoteManagementService.getDecisionsForSuggestion(suggestion, SuggestionDecision.DISAGREE);
|
||||||
|
return SuggestionInfoModel
|
||||||
|
.builder()
|
||||||
|
.agreements(agreements)
|
||||||
|
.disagreements(disagreements)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void deleteSuggestion(Long suggestionId, Long serverId) {
|
public void deleteSuggestion(Long suggestionId, Long serverId) {
|
||||||
Suggestion suggestion = suggestionManagementService.getSuggestion(serverId, suggestionId);
|
Suggestion suggestion = suggestionManagementService.getSuggestion(serverId, suggestionId);
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.service;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionDecision;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionVote;
|
||||||
|
import dev.sheldan.abstracto.suggestion.service.management.SuggestionManagementService;
|
||||||
|
import dev.sheldan.abstracto.suggestion.service.management.SuggestionVoteManagementService;
|
||||||
|
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 SuggestionVoteServiceBean implements SuggestionVoteService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SuggestionVoteManagementService suggestionVoteManagementService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserInServerManagementService userInServerManagementService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SuggestionManagementService suggestionManagementService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SuggestionVote upsertSuggestionVote(Member votingMember, SuggestionDecision decision, Long suggestionId) {
|
||||||
|
Long serverId = votingMember.getGuild().getIdLong();
|
||||||
|
Suggestion suggestion = suggestionManagementService.getSuggestion(serverId, suggestionId);
|
||||||
|
return upsertSuggestionVote(votingMember, decision, suggestion);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SuggestionVote upsertSuggestionVote(Member votingMember, SuggestionDecision decision, Suggestion suggestion) {
|
||||||
|
AUserInAServer votingUser = userInServerManagementService.loadOrCreateUser(votingMember);
|
||||||
|
Optional<SuggestionVote> suggestionVoteOptional = suggestionVoteManagementService.getSuggestionVote(votingUser, suggestion);
|
||||||
|
if(decision.equals(SuggestionDecision.REMOVE_VOTE)) {
|
||||||
|
deleteSuggestionVote(votingMember, suggestion);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(suggestionVoteOptional.isPresent()) {
|
||||||
|
log.info("Updating suggestion decision of user {} on suggestion {} in server {} to {}.", votingMember.getIdLong(),
|
||||||
|
suggestion.getSuggestionId().getId(), suggestion.getServer().getId(), decision);
|
||||||
|
SuggestionVote updatedVote = suggestionVoteOptional.get();
|
||||||
|
updatedVote.setDecision(decision);
|
||||||
|
return updatedVote;
|
||||||
|
} else {
|
||||||
|
return suggestionVoteManagementService.createSuggestionVote(votingUser, suggestion, decision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteSuggestionVote(Member votingMember, Long suggestionId) {
|
||||||
|
Suggestion suggestion = suggestionManagementService.getSuggestion(votingMember.getGuild().getIdLong(), suggestionId);
|
||||||
|
deleteSuggestionVote(votingMember, suggestion);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteSuggestionVote(Member votingMember, Suggestion suggestion) {
|
||||||
|
AUserInAServer votingUser = userInServerManagementService.loadOrCreateUser(votingMember);
|
||||||
|
log.info("Removing suggestion vote from user {} on suggestion {} in server {}.",
|
||||||
|
votingMember.getIdLong(), suggestion.getSuggestionId().getId(), suggestion.getServer().getId());
|
||||||
|
suggestionVoteManagementService.deleteSuggestionVote(votingUser, suggestion);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -101,4 +101,9 @@ public class SuggestionManagementServiceBean implements SuggestionManagementServ
|
|||||||
public List<Suggestion> getSuggestionsUpdatedBeforeNotNew(Instant date) {
|
public List<Suggestion> getSuggestionsUpdatedBeforeNotNew(Instant date) {
|
||||||
return suggestionRepository.findByUpdatedLessThanAndStateNot(date, SuggestionState.NEW);
|
return suggestionRepository.findByUpdatedLessThanAndStateNot(date, SuggestionState.NEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Suggestion> findSuggestionByMessageId(Long messageId) {
|
||||||
|
return suggestionRepository.findByMessageId(messageId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.service.management;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionDecision;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionVote;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.embed.SuggestionVoterId;
|
||||||
|
import dev.sheldan.abstracto.suggestion.repository.SuggestionVoteRepository;
|
||||||
|
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 SuggestionVoteManagementServiceBean implements SuggestionVoteManagementService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SuggestionVoteRepository suggestionVoteRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<SuggestionVote> getSuggestionVote(AUserInAServer aUserInAServer, Suggestion suggestion) {
|
||||||
|
SuggestionVoterId suggestionVoteId = SuggestionVoterId
|
||||||
|
.builder()
|
||||||
|
.suggestionId(suggestion.getSuggestionId().getId())
|
||||||
|
.serverId(suggestion.getServer().getId())
|
||||||
|
.voterId(aUserInAServer.getUserInServerId())
|
||||||
|
.build();
|
||||||
|
return suggestionVoteRepository.findById(suggestionVoteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteSuggestionVote(AUserInAServer aUserInAServer, Suggestion suggestion) {
|
||||||
|
Optional<SuggestionVote> voteOptional = getSuggestionVote(aUserInAServer, suggestion);
|
||||||
|
voteOptional.ifPresent(suggestionVote -> suggestionVoteRepository.delete(suggestionVote));
|
||||||
|
|
||||||
|
if(!voteOptional.isPresent()) {
|
||||||
|
log.warn("User {} in server {} did not have a vote for suggestion {}.",
|
||||||
|
aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId(), suggestion.getSuggestionId().getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SuggestionVote createSuggestionVote(AUserInAServer aUserInAServer, Suggestion suggestion, SuggestionDecision decision) {
|
||||||
|
SuggestionVoterId suggestionVoteId = SuggestionVoterId
|
||||||
|
.builder()
|
||||||
|
.suggestionId(suggestion.getSuggestionId().getId())
|
||||||
|
.serverId(suggestion.getServer().getId())
|
||||||
|
.voterId(aUserInAServer.getUserInServerId())
|
||||||
|
.build();
|
||||||
|
SuggestionVote vote = SuggestionVote
|
||||||
|
.builder()
|
||||||
|
.suggestionVoteId(suggestionVoteId)
|
||||||
|
.voter(aUserInAServer)
|
||||||
|
.decision(decision)
|
||||||
|
.suggestion(suggestion)
|
||||||
|
.build();
|
||||||
|
log.info("Creating suggestion decision of user {} on suggestion {} in server {} to {}.", aUserInAServer.getUserReference().getId(),
|
||||||
|
suggestion.getSuggestionId().getId(), suggestion.getServer().getId(), decision);
|
||||||
|
return suggestionVoteRepository.save(vote);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getDecisionsForSuggestion(Suggestion suggestion, SuggestionDecision decision) {
|
||||||
|
return suggestionVoteRepository.countByDecisionAndSuggestionVoteId_SuggestionIdAndSuggestionVoteId_ServerId(decision, suggestion.getSuggestionId().getId(), suggestion.getSuggestionId().getServerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteSuggestionVotes(Suggestion suggestion) {
|
||||||
|
suggestionVoteRepository.deleteBySuggestionVoteId_SuggestionIdAndSuggestionVoteId_ServerId(suggestion.getSuggestionId().getId(), suggestion.getSuggestionId().getServerId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,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="utilityModule" value="(SELECT id FROM module WHERE name = 'utility')"/>
|
||||||
|
<property name="suggestionFeature" value="(SELECT id FROM feature WHERE key = 'suggestion')"/>
|
||||||
|
|
||||||
|
<changeSet author="Sheldan" id="suggestionInfo-commands">
|
||||||
|
<insert tableName="command">
|
||||||
|
<column name="name" value="showSuggestion"/>
|
||||||
|
<column name="module_id" valueComputed="${utilityModule}"/>
|
||||||
|
<column name="feature_id" valueComputed="${suggestionFeature}"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -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="command.xml" relativeToChangelogFile="true"/>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?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="suggestion_vote-table">
|
||||||
|
<createTable tableName="suggestion_vote">
|
||||||
|
<column name="voter_user_in_server_id" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="suggestion_id" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="server_id" type="BIGINT">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="decision" type="VARCHAR(255)">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
|
||||||
|
</createTable>
|
||||||
|
<addPrimaryKey columnNames="voter_user_in_server_id, suggestion_id, server_id" tableName="suggestion_vote" constraintName="pk_suggestion_vote" validate="false"/>
|
||||||
|
<addForeignKeyConstraint baseColumnNames="suggestion_id, server_id" baseTableName="suggestion_vote" constraintName="fk_suggestion_vote_suggestion"
|
||||||
|
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||||
|
referencedColumnNames="id, server_id" referencedTableName="suggestion" validate="false"/>
|
||||||
|
<addForeignKeyConstraint baseColumnNames="voter_user_in_server_id" baseTableName="suggestion_vote" constraintName="fk_suggestion_vote_voter"
|
||||||
|
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||||
|
referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="false"/>
|
||||||
|
<sql>
|
||||||
|
DROP TRIGGER IF EXISTS suggestion_vote_update_trigger ON suggestion_vote;
|
||||||
|
CREATE TRIGGER suggestion_vote_update_trigger BEFORE UPDATE ON suggestion_vote FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||||
|
</sql>
|
||||||
|
<sql>
|
||||||
|
DROP TRIGGER IF EXISTS suggestion_vote_insert_trigger ON suggestion_vote;
|
||||||
|
CREATE TRIGGER suggestion_vote_insert_trigger BEFORE INSERT ON suggestion_vote FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||||
|
</sql>
|
||||||
|
<sql>
|
||||||
|
ALTER TABLE suggestion_vote ADD CONSTRAINT check_suggestion_vote_state CHECK (decision IN ('AGREE','DISAGREE'));
|
||||||
|
</sql>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -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="suggestion_vote.xml" relativeToChangelogFile="true"/>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -9,4 +9,5 @@
|
|||||||
<include file="1.0-suggestion/collection.xml" relativeToChangelogFile="true"/>
|
<include file="1.0-suggestion/collection.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="1.2.12/collection.xml" relativeToChangelogFile="true"/>
|
<include file="1.2.12/collection.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="1.2.13/collection.xml" relativeToChangelogFile="true"/>
|
<include file="1.2.13/collection.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="1.3.8/collection.xml" relativeToChangelogFile="true"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
@@ -12,4 +12,8 @@ abstracto.featureModes.suggestionReminder.mode=suggestionReminder
|
|||||||
abstracto.featureModes.suggestionReminder.enabled=false
|
abstracto.featureModes.suggestionReminder.enabled=false
|
||||||
|
|
||||||
abstracto.systemConfigs.suggestionReminderDays.name=suggestionReminderDays
|
abstracto.systemConfigs.suggestionReminderDays.name=suggestionReminderDays
|
||||||
abstracto.systemConfigs.suggestionReminderDays.longValue=7
|
abstracto.systemConfigs.suggestionReminderDays.longValue=7
|
||||||
|
|
||||||
|
abstracto.featureModes.suggestionButton.featureName=suggestion
|
||||||
|
abstracto.featureModes.suggestionButton.mode=suggestionButton
|
||||||
|
abstracto.featureModes.suggestionButton.enabled=true
|
||||||
@@ -19,6 +19,7 @@ import dev.sheldan.abstracto.suggestion.exception.SuggestionNotFoundException;
|
|||||||
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
|
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
|
||||||
import dev.sheldan.abstracto.suggestion.model.database.SuggestionState;
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionState;
|
||||||
import dev.sheldan.abstracto.suggestion.model.template.SuggestionLog;
|
import dev.sheldan.abstracto.suggestion.model.template.SuggestionLog;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.template.SuggestionUpdateModel;
|
||||||
import dev.sheldan.abstracto.suggestion.service.management.SuggestionManagementService;
|
import dev.sheldan.abstracto.suggestion.service.management.SuggestionManagementService;
|
||||||
import net.dv8tion.jda.api.entities.*;
|
import net.dv8tion.jda.api.entities.*;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -123,8 +124,6 @@ public class SuggestionServiceBeanTest {
|
|||||||
when(member.getGuild()).thenReturn(guild);
|
when(member.getGuild()).thenReturn(guild);
|
||||||
when(member.getIdLong()).thenReturn(SUGGESTER_ID);
|
when(member.getIdLong()).thenReturn(SUGGESTER_ID);
|
||||||
testUnit.createSuggestionMessage(message, suggestionText);
|
testUnit.createSuggestionMessage(message, suggestionText);
|
||||||
verify(reactionService, times(1)).addReactionToMessageAsync(SuggestionServiceBean.SUGGESTION_YES_EMOTE, SERVER_ID, suggestionMessage);
|
|
||||||
verify(reactionService, times(1)).addReactionToMessageAsync(SuggestionServiceBean.SUGGESTION_NO_EMOTE, SERVER_ID, suggestionMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -139,10 +138,6 @@ public class SuggestionServiceBeanTest {
|
|||||||
verify(suggestionManagementService, times(1)).createSuggestion(member, text, message, SUGGESTION_ID, commandMessage);
|
verify(suggestionManagementService, times(1)).createSuggestion(member, text, message, SUGGESTION_ID, commandMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAcceptExistingSuggestion() {
|
|
||||||
executeAcceptWithMember();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = SuggestionNotFoundException.class)
|
@Test(expected = SuggestionNotFoundException.class)
|
||||||
public void testAcceptNotExistingSuggestion() {
|
public void testAcceptNotExistingSuggestion() {
|
||||||
@@ -152,21 +147,6 @@ public class SuggestionServiceBeanTest {
|
|||||||
testUnit.acceptSuggestion(SUGGESTION_ID, message, CLOSING_TEXT);
|
testUnit.acceptSuggestion(SUGGESTION_ID, message, CLOSING_TEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUpdateSuggestionMessage() {
|
|
||||||
SuggestionLog log = Mockito.mock(SuggestionLog.class);
|
|
||||||
when(log.getServerId()).thenReturn(SERVER_ID);
|
|
||||||
MessageToSend updatedMessage = Mockito.mock(MessageToSend.class);
|
|
||||||
when(templateService.renderEmbedTemplate(eq(SuggestionServiceBean.SUGGESTION_UPDATE_TEMPLATE), any(SuggestionLog.class), eq(SERVER_ID))).thenReturn(updatedMessage);
|
|
||||||
testUnit.updateSuggestionMessageText(CLOSING_TEXT, log);
|
|
||||||
verify(postTargetService, times(1)).sendEmbedInPostTarget(updatedMessage, SuggestionPostTarget.SUGGESTION, SERVER_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRejectExistingSuggestion() {
|
|
||||||
executeRejectWithMember();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = SuggestionNotFoundException.class)
|
@Test(expected = SuggestionNotFoundException.class)
|
||||||
public void testRejectNotExistingSuggestion() {
|
public void testRejectNotExistingSuggestion() {
|
||||||
when(suggestionManagementService.getSuggestion(SERVER_ID, SUGGESTION_ID)).thenThrow(new SuggestionNotFoundException(SUGGESTION_ID));
|
when(suggestionManagementService.getSuggestion(SERVER_ID, SUGGESTION_ID)).thenThrow(new SuggestionNotFoundException(SUGGESTION_ID));
|
||||||
@@ -175,42 +155,4 @@ public class SuggestionServiceBeanTest {
|
|||||||
testUnit.rejectSuggestion(SUGGESTION_ID, message, CLOSING_TEXT);
|
testUnit.rejectSuggestion(SUGGESTION_ID, message, CLOSING_TEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeAcceptWithMember() {
|
|
||||||
Long messageId = 7L;
|
|
||||||
when(guild.getIdLong()).thenReturn(SERVER_ID);
|
|
||||||
Suggestion suggestionToAccept = setupClosing(messageId);
|
|
||||||
when(message.getGuild()).thenReturn(guild);
|
|
||||||
when(message.getMember()).thenReturn(member);
|
|
||||||
testUnit.acceptSuggestion(SUGGESTION_ID, message, CLOSING_TEXT);
|
|
||||||
verify(suggestionManagementService, times(1)).setSuggestionState(suggestionToAccept, SuggestionState.ACCEPTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void executeRejectWithMember() {
|
|
||||||
Long messageId = 7L;
|
|
||||||
when(guild.getIdLong()).thenReturn(SERVER_ID);
|
|
||||||
Suggestion suggestionToAccept = setupClosing(messageId);
|
|
||||||
when(message.getGuild()).thenReturn(guild);
|
|
||||||
when(message.getMember()).thenReturn(member);
|
|
||||||
testUnit.rejectSuggestion(SUGGESTION_ID, message, CLOSING_TEXT);
|
|
||||||
verify(suggestionManagementService, times(1)).setSuggestionState(suggestionToAccept, SuggestionState.REJECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Suggestion setupClosing(Long messageId) {
|
|
||||||
Suggestion suggestionToAccept = Mockito.mock(Suggestion.class);
|
|
||||||
when(suggestionToAccept.getChannel()).thenReturn(channel);
|
|
||||||
when(suggestionToAccept.getServer()).thenReturn(server);
|
|
||||||
when(suggestionToAccept.getSuggester()).thenReturn(suggester);
|
|
||||||
AUser aUser = Mockito.mock(AUser.class);
|
|
||||||
when(aUser.getId()).thenReturn(USER_ID);
|
|
||||||
when(suggester.getUserReference()).thenReturn(aUser);
|
|
||||||
ServerSpecificId suggestionId = Mockito.mock(ServerSpecificId.class);
|
|
||||||
when(suggestionId.getId()).thenReturn(SUGGESTION_ID);
|
|
||||||
when(suggestionToAccept.getSuggestionId()).thenReturn(suggestionId);
|
|
||||||
when(suggestionToAccept.getMessageId()).thenReturn(messageId);
|
|
||||||
when(server.getId()).thenReturn(SERVER_ID);
|
|
||||||
when(channel.getId()).thenReturn(CHANNEL_ID);
|
|
||||||
when(userService.retrieveUserForId(USER_ID)).thenReturn(CompletableFuture.completedFuture(suggesterUser));
|
|
||||||
when(suggestionManagementService.getSuggestion(SERVER_ID, SUGGESTION_ID)).thenReturn(suggestionToAccept);
|
|
||||||
return suggestionToAccept;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ import java.util.List;
|
|||||||
@Component
|
@Component
|
||||||
public class SuggestionFeatureConfig implements FeatureConfig {
|
public class SuggestionFeatureConfig implements FeatureConfig {
|
||||||
|
|
||||||
|
public static final String SUGGESTION_AGREE_EMOTE = "suggestionYes";
|
||||||
|
public static final String SUGGESTION_DISAGREE_EMOTE = "suggestionNo";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FeatureDefinition getFeature() {
|
public FeatureDefinition getFeature() {
|
||||||
return SuggestionFeatureDefinition.SUGGEST;
|
return SuggestionFeatureDefinition.SUGGEST;
|
||||||
@@ -25,12 +28,12 @@ public class SuggestionFeatureConfig implements FeatureConfig {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getRequiredEmotes() {
|
public List<String> getRequiredEmotes() {
|
||||||
return Arrays.asList("suggestionYes", "suggestionNo");
|
return Arrays.asList(SUGGESTION_AGREE_EMOTE, SUGGESTION_DISAGREE_EMOTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<FeatureMode> getAvailableModes() {
|
public List<FeatureMode> getAvailableModes() {
|
||||||
return Arrays.asList(SuggestionFeatureMode.SUGGESTION_REMINDER);
|
return Arrays.asList(SuggestionFeatureMode.SUGGESTION_REMINDER, SuggestionFeatureMode.SUGGESTION_BUTTONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import lombok.Getter;
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public enum SuggestionFeatureMode implements FeatureMode {
|
public enum SuggestionFeatureMode implements FeatureMode {
|
||||||
SUGGESTION_REMINDER("suggestionReminder");
|
SUGGESTION_REMINDER("suggestionReminder"), SUGGESTION_BUTTONS("suggestionButton");
|
||||||
|
|
||||||
private final String key;
|
private final String key;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.model.database;
|
||||||
|
|
||||||
|
public enum SuggestionDecision {
|
||||||
|
AGREE, DISAGREE, REMOVE_VOTE
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.model.database;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.embed.SuggestionVoterId;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name="suggestion_vote")
|
||||||
|
@Builder
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class SuggestionVote {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@EmbeddedId
|
||||||
|
private SuggestionVoterId suggestionVoteId;
|
||||||
|
|
||||||
|
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
|
||||||
|
@MapsId("voterId")
|
||||||
|
@JoinColumn(name = "voter_user_in_server_id", nullable = false)
|
||||||
|
private AUserInAServer voter;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
@JoinColumns(
|
||||||
|
{
|
||||||
|
@JoinColumn(updatable = false, insertable = false, name = "suggestion_id", referencedColumnName = "id"),
|
||||||
|
@JoinColumn(updatable = false, insertable = false, name = "server_id", referencedColumnName = "server_id")
|
||||||
|
})
|
||||||
|
private Suggestion suggestion;
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
@Column(name = "decision", nullable = false)
|
||||||
|
private SuggestionDecision decision;
|
||||||
|
|
||||||
|
@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,25 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.model.database.embed;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Embeddable;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Builder
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SuggestionVoterId implements Serializable {
|
||||||
|
@Column(name = "voter_user_in_server_id")
|
||||||
|
private Long voterId;
|
||||||
|
|
||||||
|
@Column(name = "suggestion_id")
|
||||||
|
private Long suggestionId;
|
||||||
|
|
||||||
|
@Column(name = "server_id")
|
||||||
|
private Long serverId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.model.template;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.template.button.ButtonPayload;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionDecision;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Builder
|
||||||
|
public class SuggestionButtonPayload implements ButtonPayload {
|
||||||
|
private Long suggestionId;
|
||||||
|
private Long serverId;
|
||||||
|
private SuggestionDecision decision;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.model.template;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Builder
|
||||||
|
public class SuggestionInfoModel {
|
||||||
|
private Long agreements;
|
||||||
|
private Long disagreements;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package dev.sheldan.abstracto.suggestion.model.template;
|
package dev.sheldan.abstracto.suggestion.model.template;
|
||||||
|
|
||||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.core.models.template.button.ButtonConfigModel;
|
||||||
import dev.sheldan.abstracto.core.utils.MessageUtils;
|
import dev.sheldan.abstracto.core.utils.MessageUtils;
|
||||||
import dev.sheldan.abstracto.suggestion.model.database.SuggestionState;
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionState;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -25,6 +26,10 @@ public class SuggestionLog {
|
|||||||
private Long serverId;
|
private Long serverId;
|
||||||
private Long originalChannelId;
|
private Long originalChannelId;
|
||||||
private Long originalMessageId;
|
private Long originalMessageId;
|
||||||
|
private Boolean useButtons;
|
||||||
|
private ButtonConfigModel agreeButtonModel;
|
||||||
|
private ButtonConfigModel disAgreeButtonModel;
|
||||||
|
private ButtonConfigModel removeVoteButtonModel;
|
||||||
|
|
||||||
public String getOriginalMessageUrl() {
|
public String getOriginalMessageUrl() {
|
||||||
return MessageUtils.buildMessageUrl(serverId, originalChannelId , originalMessageId);
|
return MessageUtils.buildMessageUrl(serverId, originalChannelId , originalMessageId);
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.model.template;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.utils.MessageUtils;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionState;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.experimental.SuperBuilder;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@SuperBuilder
|
||||||
|
public class SuggestionUpdateModel {
|
||||||
|
private Long suggestionId;
|
||||||
|
private SuggestionState state;
|
||||||
|
private User suggester;
|
||||||
|
private Member member;
|
||||||
|
private String text;
|
||||||
|
private Message message;
|
||||||
|
private String reason;
|
||||||
|
private Long serverId;
|
||||||
|
private Long originalChannelId;
|
||||||
|
private Long originalMessageId;
|
||||||
|
private Long agreeVotes;
|
||||||
|
private Long disAgreeVotes;
|
||||||
|
|
||||||
|
public String getOriginalMessageUrl() {
|
||||||
|
return MessageUtils.buildMessageUrl(serverId, originalChannelId , originalMessageId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.suggestion.service;
|
|||||||
|
|
||||||
import dev.sheldan.abstracto.core.models.ServerSpecificId;
|
import dev.sheldan.abstracto.core.models.ServerSpecificId;
|
||||||
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
|
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.template.SuggestionInfoModel;
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
import net.dv8tion.jda.api.entities.Message;
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
|
||||||
@@ -17,4 +18,5 @@ public interface SuggestionService {
|
|||||||
void cleanUpSuggestions();
|
void cleanUpSuggestions();
|
||||||
CompletableFuture<Void> remindAboutSuggestion(ServerSpecificId suggestionId);
|
CompletableFuture<Void> remindAboutSuggestion(ServerSpecificId suggestionId);
|
||||||
void cancelSuggestionReminder(Suggestion suggestion);
|
void cancelSuggestionReminder(Suggestion suggestion);
|
||||||
|
SuggestionInfoModel getSuggestionInfo(Long serverId, Long suggestionId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.service;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionDecision;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionVote;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
|
||||||
|
public interface SuggestionVoteService {
|
||||||
|
SuggestionVote upsertSuggestionVote(Member votingMember, SuggestionDecision decision, Long suggestionId);
|
||||||
|
SuggestionVote upsertSuggestionVote(Member votingMember, SuggestionDecision decision, Suggestion suggestion);
|
||||||
|
void deleteSuggestionVote(Member votingMember, Long suggestionId);
|
||||||
|
void deleteSuggestionVote(Member votingMember, Suggestion suggestion);
|
||||||
|
}
|
||||||
@@ -20,4 +20,5 @@ public interface SuggestionManagementService {
|
|||||||
void deleteSuggestion(List<Suggestion> suggestions);
|
void deleteSuggestion(List<Suggestion> suggestions);
|
||||||
void deleteSuggestion(Suggestion suggestion);
|
void deleteSuggestion(Suggestion suggestion);
|
||||||
List<Suggestion> getSuggestionsUpdatedBeforeNotNew(Instant date);
|
List<Suggestion> getSuggestionsUpdatedBeforeNotNew(Instant date);
|
||||||
|
Optional<Suggestion> findSuggestionByMessageId(Long messageId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package dev.sheldan.abstracto.suggestion.service.management;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.Suggestion;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionDecision;
|
||||||
|
import dev.sheldan.abstracto.suggestion.model.database.SuggestionVote;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface SuggestionVoteManagementService {
|
||||||
|
Optional<SuggestionVote> getSuggestionVote(AUserInAServer aUserInAServer, Suggestion suggestion);
|
||||||
|
void deleteSuggestionVote(AUserInAServer aUserInAServer, Suggestion suggestion);
|
||||||
|
SuggestionVote createSuggestionVote(AUserInAServer aUserInAServer, Suggestion suggestion, SuggestionDecision decision);
|
||||||
|
Long getDecisionsForSuggestion(Suggestion suggestion, SuggestionDecision decision);
|
||||||
|
void deleteSuggestionVotes(Suggestion suggestion);
|
||||||
|
}
|
||||||
@@ -2,8 +2,6 @@ package dev.sheldan.abstracto.core.service;
|
|||||||
|
|
||||||
import dev.sheldan.abstracto.core.logging.OkHttpLogger;
|
import dev.sheldan.abstracto.core.logging.OkHttpLogger;
|
||||||
import dev.sheldan.abstracto.core.metric.OkHttpMetrics;
|
import dev.sheldan.abstracto.core.metric.OkHttpMetrics;
|
||||||
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
|
|
||||||
import dev.sheldan.abstracto.core.metric.service.MetricService;
|
|
||||||
import dev.sheldan.abstracto.core.models.SystemInfo;
|
import dev.sheldan.abstracto.core.models.SystemInfo;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.dv8tion.jda.api.JDA;
|
import net.dv8tion.jda.api.JDA;
|
||||||
@@ -15,9 +13,7 @@ import okhttp3.OkHttpClient;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.security.auth.login.LoginException;
|
import javax.security.auth.login.LoginException;
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.lang.management.RuntimeMXBean;
|
import java.lang.management.RuntimeMXBean;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@@ -37,14 +33,6 @@ public class BotServiceBean implements BotService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private OkHttpLogger okHttpLogger;
|
private OkHttpLogger okHttpLogger;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private MetricService metricService;
|
|
||||||
|
|
||||||
public static final String DISCORD_GATEWAY_PING = "discord.gateway.ping";
|
|
||||||
private static final CounterMetric DISCORD_GATE_WAY_PING_METRIC = CounterMetric
|
|
||||||
.builder()
|
|
||||||
.name(DISCORD_GATEWAY_PING)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void login() throws LoginException {
|
public void login() throws LoginException {
|
||||||
@@ -84,11 +72,4 @@ public class BotServiceBean implements BotService {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void postConstruct() {
|
|
||||||
metricService.registerGauge(DISCORD_GATE_WAY_PING_METRIC, this, value -> value.getInstance().getGatewayPing(),
|
|
||||||
"Gateway ping");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.sheldan.abstracto.core.service;
|
package dev.sheldan.abstracto.core.service;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.template.button.ButtonConfigModel;
|
||||||
import net.dv8tion.jda.api.entities.Emoji;
|
import net.dv8tion.jda.api.entities.Emoji;
|
||||||
import net.dv8tion.jda.api.entities.Message;
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
import net.dv8tion.jda.api.entities.TextChannel;
|
import net.dv8tion.jda.api.entities.TextChannel;
|
||||||
@@ -17,7 +18,6 @@ import java.util.stream.Collectors;
|
|||||||
@Component
|
@Component
|
||||||
public class ComponentServiceBean implements ComponentService {
|
public class ComponentServiceBean implements ComponentService {
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MessageService messageService;
|
private MessageService messageService;
|
||||||
|
|
||||||
@@ -110,6 +110,11 @@ public class ComponentServiceBean implements ComponentService {
|
|||||||
return actionRows.stream().map(ActionRow::of).collect(Collectors.toList());
|
return actionRows.stream().map(ActionRow::of).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ButtonConfigModel createButtonConfigModel() {
|
||||||
|
return ButtonConfigModel.builder().buttonId(generateComponentId()).build();
|
||||||
|
}
|
||||||
|
|
||||||
private CompletableFuture<Void> setAllButtonStatesTo(Message message, Boolean disabled) {
|
private CompletableFuture<Void> setAllButtonStatesTo(Message message, Boolean disabled) {
|
||||||
List<ActionRow> actionRows = new ArrayList<>();
|
List<ActionRow> actionRows = new ArrayList<>();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package dev.sheldan.abstracto.core.service;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.listener.AsyncStartupListener;
|
||||||
|
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
|
||||||
|
import dev.sheldan.abstracto.core.metric.service.MetricService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class DiscordStartupService implements AsyncStartupListener {
|
||||||
|
|
||||||
|
public static final String DISCORD_GATEWAY_PING = "discord.gateway.ping";
|
||||||
|
private static final CounterMetric DISCORD_GATE_WAY_PING_METRIC = CounterMetric
|
||||||
|
.builder()
|
||||||
|
.name(DISCORD_GATEWAY_PING)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MetricService metricService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BotService botService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
metricService.registerGauge(DISCORD_GATE_WAY_PING_METRIC, botService, value -> value.getInstance().getGatewayPing(),
|
||||||
|
"Gateway ping");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -304,7 +304,7 @@ public class ReactionServiceBean implements ReactionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Long userId) {
|
public CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, Long serverId, Long channelId, Long messageId, Long userId) {
|
||||||
Guild guild = guildService.getGuildById(serverId);
|
Guild guild = guildService.getGuildById(serverId);
|
||||||
Integer emoteId = emote.getId();
|
Integer emoteId = emote.getId();
|
||||||
CompletableFuture<Member> memberFuture = guild.retrieveMemberById(userId).submit();
|
CompletableFuture<Member> memberFuture = guild.retrieveMemberById(userId).submit();
|
||||||
@@ -312,13 +312,18 @@ public class ReactionServiceBean implements ReactionService {
|
|||||||
|
|
||||||
return CompletableFuture.allOf(memberFuture, messageFuture).thenCompose(aVoid ->
|
return CompletableFuture.allOf(memberFuture, messageFuture).thenCompose(aVoid ->
|
||||||
memberFuture.thenCompose(member ->
|
memberFuture.thenCompose(member ->
|
||||||
self.removeReactionOfUserFromMessageWithFuture(emoteId, messageFuture.join(), memberFuture.join())
|
self.removeReactionOfUserFromMessageAsync(emoteId, messageFuture.join(), memberFuture.join())
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId) {
|
public CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, CachedMessage cachedMessage, Member member) {
|
||||||
|
return removeReactionOfUserFromMessageAsync(emote, cachedMessage.getServerId(), cachedMessage.getChannelId(), cachedMessage.getMessageId(), member);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, Long serverId, Long channelId, Long messageId) {
|
||||||
Integer emoteId = emote.getId();
|
Integer emoteId = emote.getId();
|
||||||
CompletableFuture<Message> messageFuture = channelService.retrieveMessageInChannel(serverId, channelId, messageId);
|
CompletableFuture<Message> messageFuture = channelService.retrieveMessageInChannel(serverId, channelId, messageId);
|
||||||
return messageFuture.thenCompose(message ->
|
return messageFuture.thenCompose(message ->
|
||||||
@@ -327,14 +332,19 @@ public class ReactionServiceBean implements ReactionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Member member) {
|
public CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, Long serverId, Long channelId, Long messageId, Member member) {
|
||||||
Integer emoteId = emote.getId();
|
if(emote.getId() == null) {
|
||||||
return channelService.retrieveMessageInChannel(serverId, channelId, messageId)
|
return channelService.retrieveMessageInChannel(serverId, channelId, messageId)
|
||||||
.thenCompose(message -> self.removeReactionOfUserFromMessageWithFuture(emoteId, message, member));
|
.thenCompose(message -> self.removeReaction(message, emote.getEmoteKey(), member.getUser()));
|
||||||
|
} else {
|
||||||
|
Integer emoteId = emote.getId();
|
||||||
|
return channelService.retrieveMessageInChannel(serverId, channelId, messageId)
|
||||||
|
.thenCompose(message -> self.removeReactionOfUserFromMessageAsync(emoteId, message, member));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Message message, Member member) {
|
public CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, Message message, Member member) {
|
||||||
if(Boolean.TRUE.equals(emote.getCustom())) {
|
if(Boolean.TRUE.equals(emote.getCustom())) {
|
||||||
Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId());
|
Emote emoteById = botService.getInstance().getEmoteById(emote.getEmoteId());
|
||||||
if(emoteById == null) {
|
if(emoteById == null) {
|
||||||
@@ -350,23 +360,23 @@ public class ReactionServiceBean implements ReactionService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Member member) {
|
public CompletableFuture<Void> removeReactionOfUserFromMessageAsync(Integer emoteId, Message message, Member member) {
|
||||||
AEmote emote = emoteManagementService.loadEmote(emoteId);
|
AEmote emote = emoteManagementService.loadEmote(emoteId);
|
||||||
return removeReactionOfUserFromMessageWithFuture(emote, message, member);
|
return removeReactionOfUserFromMessageAsync(emote, message, member);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Message message, Long userId) {
|
public CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, Message message, Long userId) {
|
||||||
Integer emoteId = emote.getId();
|
Integer emoteId = emote.getId();
|
||||||
return message.getGuild().retrieveMemberById(userId).submit().thenCompose(member ->
|
return message.getGuild().retrieveMemberById(userId).submit().thenCompose(member ->
|
||||||
self.removeReactionOfUserFromMessageWithFuture(emoteId, message, member)
|
self.removeReactionOfUserFromMessageAsync(emoteId, message, member)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Long userId) {
|
public CompletableFuture<Void> removeReactionOfUserFromMessageAsync(Integer emoteId, Message message, Long userId) {
|
||||||
return message.getGuild().retrieveMemberById(userId).submit().thenCompose(member ->
|
return message.getGuild().retrieveMemberById(userId).submit().thenCompose(member ->
|
||||||
self.removeReactionOfUserFromMessageWithFuture(emoteId, message, member)
|
self.removeReactionOfUserFromMessageAsync(emoteId, message, member)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.sheldan.abstracto.core.service;
|
package dev.sheldan.abstracto.core.service;
|
||||||
|
|
||||||
|
import dev.sheldan.abstracto.core.models.template.button.ButtonConfigModel;
|
||||||
import net.dv8tion.jda.api.entities.Message;
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
import net.dv8tion.jda.api.entities.TextChannel;
|
import net.dv8tion.jda.api.entities.TextChannel;
|
||||||
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||||
@@ -21,5 +22,5 @@ public interface ComponentService {
|
|||||||
CompletableFuture<Void> removeComponentWithId(Message message, String componentId);
|
CompletableFuture<Void> removeComponentWithId(Message message, String componentId);
|
||||||
CompletableFuture<Void> removeComponentWithId(Message message, String componentId, Boolean rearrange);
|
CompletableFuture<Void> removeComponentWithId(Message message, String componentId, Boolean rearrange);
|
||||||
List<ActionRow> splitIntoActionRowsMax(List<Component> components);
|
List<ActionRow> splitIntoActionRowsMax(List<Component> components);
|
||||||
|
ButtonConfigModel createButtonConfigModel();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,13 +42,14 @@ public interface ReactionService {
|
|||||||
CompletableFuture<Void> removeReactionFromMessageWithFuture(Integer emoteId, Message message);
|
CompletableFuture<Void> removeReactionFromMessageWithFuture(Integer emoteId, Message message);
|
||||||
CompletableFuture<Void> clearReactionFromMessageWithFuture(Integer emoteId, Message message);
|
CompletableFuture<Void> clearReactionFromMessageWithFuture(Integer emoteId, Message message);
|
||||||
CompletableFuture<Void> removeReactionFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId);
|
CompletableFuture<Void> removeReactionFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId);
|
||||||
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Long userId);
|
CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, Long serverId, Long channelId, Long messageId, Long userId);
|
||||||
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId);
|
CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, CachedMessage cachedMessage, Member member);
|
||||||
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId, Member member);
|
CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, Long serverId, Long channelId, Long messageId);
|
||||||
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Message message, Member member);
|
CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, Long serverId, Long channelId, Long messageId, Member member);
|
||||||
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Member member);
|
CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, Message message, Member member);
|
||||||
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(AEmote emote, Message message, Long userId);
|
CompletableFuture<Void> removeReactionOfUserFromMessageAsync(Integer emoteId, Message message, Member member);
|
||||||
CompletableFuture<Void> removeReactionOfUserFromMessageWithFuture(Integer emoteId, Message message, Long userId);
|
CompletableFuture<Void> removeReactionOfUserFromMessageAsync(AEmote emote, Message message, Long userId);
|
||||||
|
CompletableFuture<Void> removeReactionOfUserFromMessageAsync(Integer emoteId, Message message, Long userId);
|
||||||
CompletableFuture<Void> clearReactionFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId);
|
CompletableFuture<Void> clearReactionFromMessageWithFuture(AEmote emote, Long serverId, Long channelId, Long messageId);
|
||||||
List<CompletableFuture<Void>> addReactionsToMessageWithFuture(List<String> emoteKeys, Long serverId, Message message);
|
List<CompletableFuture<Void>> addReactionsToMessageWithFuture(List<String> emoteKeys, Long serverId, Message message);
|
||||||
List<CompletableFuture<Void>> removeReactionFromMessagesWithFuture(List<Message> messages, Integer emoteId);
|
List<CompletableFuture<Void>> removeReactionFromMessagesWithFuture(List<Message> messages, Integer emoteId);
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ Feature key: `suggestion`
|
|||||||
|
|
||||||
==== Feature modes
|
==== Feature modes
|
||||||
`suggestionReminder`:: If this is enabled, a message will be sent to the post target `suggestionReminder`, after the amount of days configured in `suggestionReminderDays`. Disabled by default.
|
`suggestionReminder`:: If this is enabled, a message will be sent to the post target `suggestionReminder`, after the amount of days configured in `suggestionReminderDays`. Disabled by default.
|
||||||
|
`suggestionButton` :: Use buttons instead of reactions for suggestions. Enabled by default.
|
||||||
|
|
||||||
==== Post targets
|
==== Post targets
|
||||||
`suggestions`:: the target of the messages containing the suggestions
|
`suggestions`:: the target of the messages containing the suggestions
|
||||||
|
|||||||
Reference in New Issue
Block a user