[SIS-6] adding ability to declare "no_time" for a meetup

adding meetup id to meetup message
refactoring meetup cancellation to be a command
adding command to change the meetup time and notify meetup members
fixing meetup components not being cleaned when cancelling or cleaning up
changing label for no button
This commit is contained in:
Sheldan
2022-07-17 18:34:06 +02:00
parent f688a066b4
commit 48e2e705c2
60 changed files with 1099 additions and 57 deletions

View File

@@ -0,0 +1,111 @@
package dev.sheldan.sissi.module.meetup.commands;
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.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.sissi.module.meetup.config.MeetupFeatureDefinition;
import dev.sheldan.sissi.module.meetup.config.MeetupSlashCommandNames;
import dev.sheldan.sissi.module.meetup.exception.NotMeetupOrganizerException;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
import dev.sheldan.sissi.module.meetup.service.MeetupServiceBean;
import dev.sheldan.sissi.module.meetup.service.management.MeetupManagementServiceBean;
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 CancelMeetup extends AbstractConditionableCommand {
private static final String MEETUP_ID_PARAMETER = "meetupId";
private static final String CANCEL_MEETUP_COMMAND = "cancelMeetup";
private static final String CANCEL_MEETUP_RESPONSE = "cancelMeetup_response";
@Autowired
private MeetupServiceBean meetupServiceBean;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private MeetupManagementServiceBean meetupManagementServiceBean;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
Long meetupId = (Long) commandContext.getParameters().getParameters().get(0);
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, commandContext.getGuild().getIdLong());
if(!meetup.getOrganizer().getUserReference().getId().equals(commandContext.getAuthor().getIdLong())) {
throw new NotMeetupOrganizerException();
}
return meetupServiceBean.cancelMeetup(meetup)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
event.deferReply().queue();
Long meetupId = slashCommandParameterService.getCommandOption(MEETUP_ID_PARAMETER, event, Integer.class).longValue();
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, event.getGuild().getIdLong());
if(!meetup.getOrganizer().getUserReference().getId().equals(event.getMember().getIdLong())) {
throw new NotMeetupOrganizerException();
}
return meetupServiceBean.cancelMeetup(meetup)
.thenCompose(unused -> FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(CANCEL_MEETUP_RESPONSE, new Object(), event.getHook())))
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter meetupIdParameter = Parameter
.builder()
.templated(true)
.name(MEETUP_ID_PARAMETER)
.type(Long.class)
.build();
List<Parameter> parameters = Arrays.asList(meetupIdParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(MeetupSlashCommandNames.MEETUP)
.commandName("cancel")
.build();
return CommandConfiguration.builder()
.name(CANCEL_MEETUP_COMMAND)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return MeetupFeatureDefinition.MEETUP;
}
}

View File

@@ -0,0 +1,173 @@
package dev.sheldan.sissi.module.meetup.commands;
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.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.ComponentService;
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.sissi.module.meetup.config.MeetupFeatureDefinition;
import dev.sheldan.sissi.module.meetup.config.MeetupSlashCommandNames;
import dev.sheldan.sissi.module.meetup.exception.MeetupPastTimeException;
import dev.sheldan.sissi.module.meetup.exception.NotMeetupOrganizerException;
import dev.sheldan.sissi.module.meetup.model.command.MeetupChangeTimeConfirmationModel;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
import dev.sheldan.sissi.module.meetup.service.MeetupServiceBean;
import dev.sheldan.sissi.module.meetup.service.management.MeetupManagementServiceBean;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class ChangeMeetupTime extends AbstractConditionableCommand {
private static final String CHANGE_MEETUP_TIME_COMMAND = "changeMeetupTime";
private static final String MEETUP_ID_PARAMETER = "meetupId";
private static final String MEETUP_NEW_TIMESTAMP_PARAMETER = "newTimeStamp";
private static final String CHANGE_MEETUP_TIME_CONFIRMATION = "changeMeetupTime_confirmation";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private MeetupManagementServiceBean meetupManagementServiceBean;
@Autowired
private ComponentService componentService;
@Autowired
private InteractionService interactionService;
@Autowired
private MeetupServiceBean meetupServiceBean;
@Autowired
private ChannelService channelService;
@Autowired
private TemplateService templateService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Long meetupId = (Long) parameters.get(0);
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, commandContext.getGuild().getIdLong());
if(!meetup.getOrganizer().getUserReference().getId().equals(commandContext.getAuthor().getIdLong())) {
throw new NotMeetupOrganizerException();
}
Long newTimestamp = (Long) parameters.get(1);
Instant newMeetupTime = Instant.ofEpochSecond(newTimestamp);
if(newMeetupTime.isBefore(Instant.now())) {
throw new MeetupPastTimeException();
}
String confirmationId = componentService.generateComponentId();
String cancelId = componentService.generateComponentId();
MeetupChangeTimeConfirmationModel model = MeetupChangeTimeConfirmationModel
.builder()
.meetupTime(newMeetupTime)
.topic(meetup.getTopic())
.description(meetup.getDescription())
.userId(commandContext.getAuthor().getIdLong())
.guildId(commandContext.getGuild().getIdLong())
.meetupId(meetupId)
.confirmationId(confirmationId)
.cancelId(cancelId)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(CHANGE_MEETUP_TIME_CONFIRMATION, model, commandContext.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenAccept(unused -> meetupServiceBean.storeMeetupChangeTimeConfirmation(model))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long meetupId = slashCommandParameterService.getCommandOption(MEETUP_ID_PARAMETER, event, Integer.class).longValue();
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, event.getGuild().getIdLong());
if(!meetup.getOrganizer().getUserReference().getId().equals(event.getMember().getIdLong())) {
throw new NotMeetupOrganizerException();
}
Integer time = slashCommandParameterService.getCommandOption(MEETUP_NEW_TIMESTAMP_PARAMETER, event, Long.class, Integer.class);
Instant meetupTime = Instant.ofEpochSecond(time);
if(meetupTime.isBefore(Instant.now())) {
throw new MeetupPastTimeException();
}
String confirmationId = componentService.generateComponentId();
String cancelId = componentService.generateComponentId();
MeetupChangeTimeConfirmationModel model = MeetupChangeTimeConfirmationModel
.builder()
.meetupTime(meetupTime)
.topic(meetup.getTopic())
.description(meetup.getDescription())
.userId(event.getMember().getIdLong())
.guildId(event.getGuild().getIdLong())
.meetupId(meetupId)
.confirmationId(confirmationId)
.cancelId(cancelId)
.build();
return interactionService.replyEmbed(CHANGE_MEETUP_TIME_CONFIRMATION, model, event)
.thenAccept(interactionHook -> meetupServiceBean.storeMeetupChangeTimeConfirmation(model))
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter meetupIdParameter = Parameter
.builder()
.templated(true)
.name(MEETUP_ID_PARAMETER)
.type(Long.class)
.build();
Parameter newTimeStampParameter = Parameter
.builder()
.templated(true)
.name(MEETUP_NEW_TIMESTAMP_PARAMETER)
.type(Long.class)
.build();
List<Parameter> parameters = Arrays.asList(meetupIdParameter, newTimeStampParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(MeetupSlashCommandNames.MEETUP)
.commandName("changeTime")
.build();
return CommandConfiguration.builder()
.name(CHANGE_MEETUP_TIME_COMMAND)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return MeetupFeatureDefinition.MEETUP;
}
}

View File

@@ -86,7 +86,7 @@ public class ListMeetups extends AbstractConditionableCommand {
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(MeetupSlashCommandNames.MEETUP)
.rootCommandName(MeetupSlashCommandNames.MEETUP_PUBLIC)
.commandName("list")
.build();

View File

@@ -0,0 +1,121 @@
package dev.sheldan.sissi.module.meetup.commands;
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.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.sissi.module.meetup.config.MeetupFeatureDefinition;
import dev.sheldan.sissi.module.meetup.config.MeetupSlashCommandNames;
import dev.sheldan.sissi.module.meetup.exception.NotMeetupOrganizerException;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
import dev.sheldan.sissi.module.meetup.service.MeetupServiceBean;
import dev.sheldan.sissi.module.meetup.service.management.MeetupManagementServiceBean;
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 NotifyMeetupParticipants extends AbstractConditionableCommand {
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private MeetupManagementServiceBean meetupManagementServiceBean;
@Autowired
private MeetupServiceBean meetupServiceBean;
@Autowired
private InteractionService interactionService;
private static final String MEETUP_ID_PARAMETER = "meetupId";
private static final String NOTIFICATION_MESSAGE_PARAMETER = "notificationMessage";
private static final String NOTIFY_MEETUP_PARTICIPANTS_COMMAND = "notifyMeetupParticipants";
private static final String NOTIFY_MEETUP_PARTICIPANTS_RESPONSE = "notifyMeetupParticipants_response";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Long meetupId = (Long) parameters.get(0);
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, commandContext.getGuild().getIdLong());
if(!meetup.getOrganizer().getUserReference().getId().equals(commandContext.getAuthor().getIdLong())) {
throw new NotMeetupOrganizerException();
}
String notificationMessage = (String) parameters.get(1);
return meetupServiceBean.notifyMeetupParticipants(meetup, notificationMessage)
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long meetupId = slashCommandParameterService.getCommandOption(MEETUP_ID_PARAMETER, event, Integer.class).longValue();
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, event.getGuild().getIdLong());
if(!meetup.getOrganizer().getUserReference().getId().equals(event.getMember().getIdLong())) {
throw new NotMeetupOrganizerException();
}
String notificationMessage = slashCommandParameterService.getCommandOption(NOTIFICATION_MESSAGE_PARAMETER, event, String.class);
return meetupServiceBean.notifyMeetupParticipants(meetup, notificationMessage)
.thenCompose(unused -> interactionService.replyEmbed(NOTIFY_MEETUP_PARTICIPANTS_RESPONSE, event))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter meetupIdParameter = Parameter
.builder()
.templated(true)
.name(MEETUP_ID_PARAMETER)
.type(Long.class)
.build();
Parameter notificationMessage = Parameter
.builder()
.templated(true)
.name(NOTIFICATION_MESSAGE_PARAMETER)
.type(String.class)
.remainder(true)
.build();
List<Parameter> parameters = Arrays.asList(meetupIdParameter, notificationMessage);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(MeetupSlashCommandNames.MEETUP)
.commandName("notify")
.build();
return CommandConfiguration.builder()
.name(NOTIFY_MEETUP_PARTICIPANTS_COMMAND)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return MeetupFeatureDefinition.MEETUP;
}
}

View File

@@ -2,4 +2,5 @@ package dev.sheldan.sissi.module.meetup.config;
public class MeetupSlashCommandNames {
public static final String MEETUP = "meetup";
public static final String MEETUP_PUBLIC = "meetupPublic";
}

View File

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

View File

@@ -0,0 +1,78 @@
package dev.sheldan.sissi.module.meetup.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.ListenerPriority;
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.service.MessageService;
import dev.sheldan.abstracto.core.service.management.ComponentPayloadManagementService;
import dev.sheldan.sissi.module.meetup.config.MeetupFeatureDefinition;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
import dev.sheldan.sissi.module.meetup.model.payload.MeetupChangeTimeConfirmationPayload;
import dev.sheldan.sissi.module.meetup.service.MeetupServiceBean;
import dev.sheldan.sissi.module.meetup.service.management.MeetupManagementServiceBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
@Component
@Slf4j
public class MeetupChangeTimeConfirmationListener implements ButtonClickedListener {
@Autowired
private MeetupManagementServiceBean meetupManagementServiceBean;
@Autowired
private MessageService messageService;
@Autowired
private ComponentPayloadManagementService componentPayloadManagementService;
@Autowired
private MeetupServiceBean meetupServiceBean;
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
MeetupChangeTimeConfirmationPayload payload = (MeetupChangeTimeConfirmationPayload) model.getDeserializedPayload();
if(model.getEvent().getUser().getIdLong() != payload.getOrganizerUserId()) {
return ButtonClickedListenerResult.IGNORED;
}
if(model.getEvent().getComponentId().equals(payload.getConfirmationId())) {
Meetup meetup = meetupManagementServiceBean.getMeetup(payload.getMeetupId(), payload.getGuildId());
meetupServiceBean.changeMeetupTimeAndNotifyParticipants(meetup, Instant.ofEpochSecond(payload.getNewTime()));
messageService.deleteMessage(model.getEvent().getMessage());
cleanupConfirmationMessagePayloads(payload);
} else if(model.getEvent().getComponentId().equals(payload.getCancelId())) {
messageService.deleteMessage(model.getEvent().getMessage());
cleanupConfirmationMessagePayloads(payload);
return ButtonClickedListenerResult.ACKNOWLEDGED;
} else {
return ButtonClickedListenerResult.IGNORED;
}
return ButtonClickedListenerResult.IGNORED;
}
private void cleanupConfirmationMessagePayloads(MeetupChangeTimeConfirmationPayload payload) {
componentPayloadManagementService.deletePayload(payload.getCancelId());
componentPayloadManagementService.deletePayload(payload.getConfirmationId());
}
@Override
public Boolean handlesEvent(ButtonClickedListenerModel model) {
return model.getOrigin().equals(MeetupServiceBean.MEETUP_CHANGE_TIME_CONFIRMATION_BUTTON) &&
model.getEvent().isFromGuild();
}
@Override
public FeatureDefinition getFeature() {
return MeetupFeatureDefinition.MEETUP;
}
@Override
public Integer getPriority() {
return ListenerPriority.LOWEST;
}
}

View File

@@ -6,6 +6,7 @@ import dev.sheldan.abstracto.core.listener.ButtonClickedListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.ButtonClickedListener;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.ComponentPayload;
import dev.sheldan.abstracto.core.models.listener.ButtonClickedListenerModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.ComponentPayloadService;
@@ -24,6 +25,7 @@ import dev.sheldan.sissi.module.meetup.model.payload.MeetupDecisionPayload;
import dev.sheldan.sissi.module.meetup.model.payload.MeetupConfirmationPayload;
import dev.sheldan.sissi.module.meetup.model.template.MeetupMessageModel;
import dev.sheldan.sissi.module.meetup.service.MeetupServiceBean;
import dev.sheldan.sissi.module.meetup.service.management.MeetupComponentManagementServiceBean;
import dev.sheldan.sissi.module.meetup.service.management.MeetupManagementServiceBean;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
@@ -71,6 +73,9 @@ public class MeetupConfirmationListener implements ButtonClickedListener {
@Autowired
private ComponentPayloadManagementService componentPayloadManagementService;
@Autowired
private MeetupComponentManagementServiceBean meetupComponentManagementServiceBean;
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
MeetupConfirmationPayload payload = (MeetupConfirmationPayload) model.getDeserializedPayload();
@@ -83,22 +88,22 @@ public class MeetupConfirmationListener implements ButtonClickedListener {
} else if(model.getEvent().getComponentId().equals(payload.getCancelId())){
meetup.setState(MeetupState.CANCELLED);
messageService.deleteMessage(model.getEvent().getMessage());
cleanupConfirmationMessage(payload);
cleanupConfirmationMessagePayloads(payload);
meetupManagementServiceBean.deleteMeetup(meetup);
return ButtonClickedListenerResult.ACKNOWLEDGED;
} else {
return ButtonClickedListenerResult.IGNORED;
}
cleanupConfirmationMessage(payload);
cleanupConfirmationMessagePayloads(payload);
String yesButtonId = componentService.generateComponentId();
String noButtonId = componentService.generateComponentId();
String maybeButtonId = componentService.generateComponentId();
String cancelButtonId = componentService.generateComponentId();
String noTimeButtonId = componentService.generateComponentId();
MeetupMessageModel messageModel = meetupServiceBean.getMeetupMessageModel(meetup);
messageModel.setYesId(yesButtonId);
messageModel.setNoId(noButtonId);
messageModel.setMaybeId(maybeButtonId);
messageModel.setCancelId(cancelButtonId);
messageModel.setNoTimeId(noTimeButtonId);
messageModel.setCancelled(false);
Long meetupId = payload.getMeetupId();
Long serverId = payload.getGuildId();
@@ -108,7 +113,7 @@ public class MeetupConfirmationListener implements ButtonClickedListener {
messageService.deleteMessage(model.getEvent().getMessage());
Message meetupMessage = messageFutures.get(0).join();
messageService.pinMessage(meetupMessage);
self.persistPayloads(meetupId, serverId, yesButtonId, noButtonId, maybeButtonId, cancelButtonId, meetupMessage);
self.persistPayloads(meetupId, serverId, yesButtonId, noButtonId, maybeButtonId, noTimeButtonId, meetupMessage);
}).exceptionally(throwable -> {
log.error("Failed to send meetup message for meetup {}.", meetupId, throwable);
return null;
@@ -116,30 +121,36 @@ public class MeetupConfirmationListener implements ButtonClickedListener {
return ButtonClickedListenerResult.ACKNOWLEDGED;
}
private void cleanupConfirmationMessage(MeetupConfirmationPayload payload) {
private void cleanupConfirmationMessagePayloads(MeetupConfirmationPayload payload) {
componentPayloadManagementService.deletePayload(payload.getCancelId());
componentPayloadManagementService.deletePayload(payload.getConfirmationId());
}
@Transactional
public void persistPayloads(Long meetupId, Long serverId, String yesButtonId, String noButtonId, String maybeButtonId, String cancelButtonId, Message meetupMessage) {
public void persistPayloads(Long meetupId, Long serverId, String yesButtonId, String noButtonId, String maybeButtonId, String noTimeButtonId, Message meetupMessage) {
MeetupDecisionPayload decisionPayload = MeetupDecisionPayload
.builder()
.meetupId(meetupId)
.guildId(serverId)
.componentPayloads(Arrays.asList(yesButtonId, noButtonId, maybeButtonId))
.componentPayloads(Arrays.asList(yesButtonId, noButtonId, maybeButtonId, noTimeButtonId))
.build();
AServer server = serverManagementService.loadServer(serverId);
decisionPayload.setMeetupDecision(MeetupDecision.YES);
componentPayloadService.createButtonPayload(yesButtonId, decisionPayload, MEETUP_DECISION_BUTTON, server);
ComponentPayload yesPayload = componentPayloadService.createButtonPayload(yesButtonId, decisionPayload, MEETUP_DECISION_BUTTON, server);
decisionPayload.setMeetupDecision(MeetupDecision.NO);
componentPayloadService.createButtonPayload(noButtonId, decisionPayload, MEETUP_DECISION_BUTTON, server);
ComponentPayload noPayload = componentPayloadService.createButtonPayload(noButtonId, decisionPayload, MEETUP_DECISION_BUTTON, server);
decisionPayload.setMeetupDecision(MeetupDecision.MAYBE);
componentPayloadService.createButtonPayload(maybeButtonId, decisionPayload, MEETUP_DECISION_BUTTON, server);
decisionPayload.setMeetupDecision(MeetupDecision.CANCEL);
componentPayloadService.createButtonPayload(cancelButtonId, decisionPayload, MEETUP_DECISION_BUTTON, server);
ComponentPayload maybePayload = componentPayloadService.createButtonPayload(maybeButtonId, decisionPayload, MEETUP_DECISION_BUTTON, server);
decisionPayload.setMeetupDecision(MeetupDecision.NO_TIME);
ComponentPayload noTimePayload = componentPayloadService.createButtonPayload(noTimeButtonId, decisionPayload, MEETUP_DECISION_BUTTON, server);
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, serverId);
// storing the button IDs, so we can remove them independently
meetupComponentManagementServiceBean.createComponent(meetup, yesButtonId, yesPayload);
meetupComponentManagementServiceBean.createComponent(meetup, noButtonId, noPayload);
meetupComponentManagementServiceBean.createComponent(meetup, maybeButtonId, maybePayload);
meetupComponentManagementServiceBean.createComponent(meetup, noTimeButtonId, noTimePayload);
meetupServiceBean.scheduleReminders(meetup);
meetup.setMessageId(meetupMessage.getIdLong());
AChannel channel = channelManagementService.loadChannel(meetupMessage.getChannel());

View File

@@ -13,7 +13,7 @@ import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.sissi.module.meetup.config.MeetupFeatureDefinition;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
import dev.sheldan.sissi.module.meetup.model.database.MeetupDecision;
import dev.sheldan.sissi.module.meetup.model.database.MeetupParticipator;
import dev.sheldan.sissi.module.meetup.model.database.MeetupParticipant;
import dev.sheldan.sissi.module.meetup.model.payload.MeetupDecisionPayload;
import dev.sheldan.sissi.module.meetup.model.template.MeetupMessageModel;
import dev.sheldan.sissi.module.meetup.service.MeetupServiceBean;
@@ -49,17 +49,9 @@ public class MeetupDecisionListener implements ButtonClickedListener {
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
MeetupDecisionPayload payload = (MeetupDecisionPayload) model.getDeserializedPayload();
Meetup meetup = meetupManagementServiceBean.getMeetup(payload.getMeetupId(), payload.getGuildId());
if(payload.getMeetupDecision().equals(MeetupDecision.CANCEL)) {
if(model.getEvent().getUser().getIdLong() == meetup.getOrganizer().getUserReference().getId()) {
meetupServiceBean.cancelMeetup(meetup, payload.getComponentPayloads());
return ButtonClickedListenerResult.ACKNOWLEDGED;
} else {
return ButtonClickedListenerResult.IGNORED;
}
}
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(model.getEvent().getMember());
Optional<MeetupParticipator> participationOptional = meetupParticipatorManagementServiceBean.getParticipation(meetup, userInAServer);
Optional<MeetupParticipant> participationOptional = meetupParticipatorManagementServiceBean.getParticipation(meetup, userInAServer);
if(participationOptional.isPresent()) {
participationOptional.get().setDecision(payload.getMeetupDecision());
} else {
@@ -84,7 +76,8 @@ public class MeetupDecisionListener implements ButtonClickedListener {
addIfMissing(model.getParticipants(), aUserInAServer);
} else if(decision.equals(MeetupDecision.MAYBE)) {
addIfMissing(model.getMaybeParticipants(), aUserInAServer);
}
} else if(decision.equals(MeetupDecision.NO_TIME))
addIfMissing(model.getNoTimeParticipants(), aUserInAServer);
}
private void addIfMissing(List<MemberDisplay> list, AUserInAServer aUserInAServer) {

View File

@@ -0,0 +1,21 @@
package dev.sheldan.sissi.module.meetup.model.command;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.Instant;
@Getter
@Setter
@Builder
public class MeetupChangeTimeConfirmationModel {
private Instant meetupTime;
private String topic;
private Long userId;
private String description;
private Long meetupId;
private Long guildId;
private String confirmationId;
private String cancelId;
}

View File

@@ -49,7 +49,15 @@ public class Meetup {
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
mappedBy = "meetup")
@Builder.Default
private List<MeetupParticipator> participants = new ArrayList<>();
private List<MeetupParticipant> participants = new ArrayList<>();
@OneToMany(
fetch = FetchType.LAZY,
orphanRemoval = true,
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
mappedBy = "meetup")
@Builder.Default
private List<MeetupComponent> meetupComponents = new ArrayList<>();
@Getter
@Enumerated(EnumType.STRING)

View File

@@ -0,0 +1,40 @@
package dev.sheldan.sissi.module.meetup.model.database;
import dev.sheldan.abstracto.core.models.database.ComponentPayload;
import dev.sheldan.sissi.module.meetup.model.database.embed.MeetupComponentId;
import lombok.*;
import javax.persistence.*;
import java.time.Instant;
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "meetup_component")
@Getter
@Setter
@EqualsAndHashCode
public class MeetupComponent {
@EmbeddedId
@Getter
private MeetupComponentId id;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
@MapsId("componentId")
@JoinColumn(name = "component_id", nullable = false)
private ComponentPayload payload;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns(
{
@JoinColumn(updatable = false, insertable = false, name = "meetup_id", referencedColumnName = "id"),
@JoinColumn(updatable = false, insertable = false, name = "server_id", referencedColumnName = "server_id")
})
private Meetup meetup;
@Column(name = "created")
private Instant created;
}

View File

@@ -1,5 +1,5 @@
package dev.sheldan.sissi.module.meetup.model.database;
public enum MeetupDecision {
YES, NO, MAYBE, CANCEL
YES, NO, MAYBE, NO_TIME
}

View File

@@ -15,14 +15,14 @@ import java.time.Instant;
@Getter
@Setter
@EqualsAndHashCode
public class MeetupParticipator {
public class MeetupParticipant {
@EmbeddedId
@Getter
private MeetupParticipationId id;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
@MapsId("voterId")
@MapsId("participatorId")
@JoinColumn(name = "meetup_participator_user_in_server_id", nullable = false)
private AUserInAServer participator;

View File

@@ -0,0 +1,25 @@
package dev.sheldan.sissi.module.meetup.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 MeetupComponentId implements Serializable {
@Column(name = "component_id")
private String componentId;
@Column(name = "meetup_id")
private Long meetupId;
@Column(name = "server_id")
private Long serverId;
}

View File

@@ -0,0 +1,18 @@
package dev.sheldan.sissi.module.meetup.model.payload;
import dev.sheldan.abstracto.core.models.template.button.ButtonPayload;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@Getter
@Setter
public class MeetupChangeTimeConfirmationPayload implements ButtonPayload {
private Long organizerUserId;
private Long meetupId;
private Long newTime;
private Long guildId;
private String confirmationId;
private String cancelId;
}

View File

@@ -16,12 +16,14 @@ public class MeetupMessageModel {
private String description;
private Instant meetupTime;
private MemberDisplay organizer;
private Long meetupId;
private String yesId;
private String noId;
private String maybeId;
private String cancelId;
private String noTimeId;
private Boolean cancelled;
private List<MemberDisplay> participants;
private List<MemberDisplay> maybeParticipants;
private List<MemberDisplay> noTimeParticipants;
private List<MemberDisplay> declinedParticipants;
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.sissi.module.meetup.model.template;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import lombok.Builder;
import lombok.Getter;
import java.util.List;
@Builder
@Getter
public class MeetupNotificationModel {
private List<MemberDisplay> participants;
private String notificationMessage;
}

View File

@@ -0,0 +1,17 @@
package dev.sheldan.sissi.module.meetup.model.template;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import lombok.Builder;
import lombok.Getter;
import java.time.Instant;
@Builder
@Getter
public class MeetupTimeChangedNotificationModel {
private String meetupTopic;
private String meetupDescription;
private Instant oldDate;
private Instant newDate;
private ServerChannelMessage meetupMessage;
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.sissi.module.meetup.repository;
import dev.sheldan.sissi.module.meetup.model.database.MeetupComponent;
import dev.sheldan.sissi.module.meetup.model.database.embed.MeetupComponentId;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MeetupComponentRepository extends JpaRepository<MeetupComponent, MeetupComponentId> {
}

View File

@@ -1,10 +1,10 @@
package dev.sheldan.sissi.module.meetup.repository;
import dev.sheldan.sissi.module.meetup.model.database.MeetupParticipator;
import dev.sheldan.sissi.module.meetup.model.database.MeetupParticipant;
import dev.sheldan.sissi.module.meetup.model.database.embed.MeetupParticipationId;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MeetupParticipatorRepository extends JpaRepository<MeetupParticipator, MeetupParticipationId> {
public interface MeetupParticipatorRepository extends JpaRepository<MeetupParticipant, MeetupParticipationId> {
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.sissi.module.meetup.service;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.*;
@@ -7,27 +8,33 @@ import dev.sheldan.abstracto.core.service.management.ComponentPayloadManagementS
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.scheduling.model.JobParameters;
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import dev.sheldan.sissi.module.meetup.model.command.MeetupChangeTimeConfirmationModel;
import dev.sheldan.sissi.module.meetup.model.command.MeetupConfirmationModel;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
import dev.sheldan.sissi.module.meetup.model.database.MeetupDecision;
import dev.sheldan.sissi.module.meetup.model.database.MeetupParticipator;
import dev.sheldan.sissi.module.meetup.model.database.MeetupParticipant;
import dev.sheldan.sissi.module.meetup.model.database.MeetupState;
import dev.sheldan.sissi.module.meetup.model.payload.MeetupChangeTimeConfirmationPayload;
import dev.sheldan.sissi.module.meetup.model.payload.MeetupConfirmationPayload;
import dev.sheldan.sissi.module.meetup.model.template.MeetupMessageModel;
import dev.sheldan.sissi.module.meetup.model.template.MeetupNotificationModel;
import dev.sheldan.sissi.module.meetup.model.template.MeetupTimeChangedNotificationModel;
import dev.sheldan.sissi.module.meetup.service.management.MeetupComponentManagementServiceBean;
import dev.sheldan.sissi.module.meetup.service.management.MeetupManagementServiceBean;
import dev.sheldan.sissi.module.meetup.service.management.MeetupParticipatorManagementServiceBean;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.GuildMessageChannel;
import net.dv8tion.jda.api.entities.MessageChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@@ -36,9 +43,11 @@ import java.util.stream.Collectors;
public class MeetupServiceBean {
public static final String MEETUP_CONFIRMATION_BUTTON = "MEETUP_CONFIRMATION_BUTTON";
public static final String MEETUP_CHANGE_TIME_CONFIRMATION_BUTTON = "MEETUP_CHANGE_TIME_CONFIRMATION_BUTTON";
public static final String MEETUP_DECISION_BUTTON = "MEETUP_DECISION_BUTTON";
private static final String MEETUP_DISPLAY_TEMPLATE = "meetup_display";
private static final String MEETUP_CANCELLATION_TEMPLATE = "meetup_cancel_notification";
private static final String MEETUP_CHANGE_TIME_NOTIFICATION_TEMPLATE = "changeMeetupTime_notification";
private static final String MEETUP_REMINDER_TEMPLATE = "meetup_reminder_notification";
private static final String MEETUP_LATE_REMINDER_CONFIG_KEY = "meetupLateReminderSeconds";
private static final String MEETUP_EARLY_REMINDER_CONFIG_KEY = "meetupEarlyReminderSeconds";
@@ -77,6 +86,12 @@ public class MeetupServiceBean {
@Autowired
private SchedulerService schedulerService;
@Autowired
private MeetupParticipatorManagementServiceBean meetupParticipatorManagementServiceBean;
@Autowired
private MeetupComponentManagementServiceBean meetupComponentManagementServiceBean;
public void storeMeetupConfirmation(MeetupConfirmationModel model) {
AServer server = serverManagementService.loadServer(model.getGuildId());
MeetupConfirmationPayload confirmationPayload = MeetupConfirmationPayload
@@ -91,35 +106,56 @@ public class MeetupServiceBean {
componentPayloadService.createButtonPayload(model.getCancelId(), confirmationPayload, MEETUP_CONFIRMATION_BUTTON, server);
}
public void storeMeetupChangeTimeConfirmation(MeetupChangeTimeConfirmationModel model) {
AServer server = serverManagementService.loadServer(model.getGuildId());
MeetupChangeTimeConfirmationPayload confirmationPayload = MeetupChangeTimeConfirmationPayload
.builder()
.newTime(model.getMeetupTime().getEpochSecond())
.confirmationId(model.getConfirmationId())
.meetupId(model.getMeetupId())
.organizerUserId(model.getUserId())
.cancelId(model.getCancelId())
.guildId(model.getGuildId())
.build();
componentPayloadService.createButtonPayload(model.getConfirmationId(), confirmationPayload, MEETUP_CHANGE_TIME_CONFIRMATION_BUTTON, server);
componentPayloadService.createButtonPayload(model.getCancelId(), confirmationPayload, MEETUP_CHANGE_TIME_CONFIRMATION_BUTTON, server);
}
public MeetupMessageModel getMeetupMessageModel(Meetup meetup) {
List<MeetupParticipator> allParticipants = meetup.getParticipants();
List<MeetupParticipator> participating = allParticipants
List<MeetupParticipant> allParticipants = meetup.getParticipants();
List<MeetupParticipant> participating = allParticipants
.stream()
.filter(meetupParticipator -> meetupParticipator.getDecision().equals(MeetupDecision.YES))
.collect(Collectors.toList());
List<MeetupParticipator> maybe = allParticipants
List<MeetupParticipant> maybe = allParticipants
.stream()
.filter(meetupParticipator -> meetupParticipator.getDecision().equals(MeetupDecision.MAYBE))
.collect(Collectors.toList());
List<MeetupParticipator> notParticipating = allParticipants
List<MeetupParticipant> notParticipating = allParticipants
.stream()
.filter(meetupParticipator -> meetupParticipator.getDecision().equals(MeetupDecision.NO))
.collect(Collectors.toList());
List<MeetupParticipant> notTimeParticipating = allParticipants
.stream()
.filter(meetupParticipator -> meetupParticipator.getDecision().equals(MeetupDecision.NO_TIME))
.collect(Collectors.toList());
return MeetupMessageModel
.builder()
.description(meetup.getDescription())
.topic(meetup.getTopic())
.meetupTime(meetup.getMeetupTime())
.meetupId(meetup.getId().getId())
.participants(getMemberDisplays(participating))
.declinedParticipants(getMemberDisplays(notParticipating))
.noTimeParticipants(getMemberDisplays(notTimeParticipating))
.maybeParticipants(getMemberDisplays(maybe))
.cancelled(meetup.getState().equals(MeetupState.CANCELLED))
.organizer(MemberDisplay.fromAUserInAServer(meetup.getOrganizer()))
.build();
}
private List<MemberDisplay> getMemberDisplays(List<MeetupParticipator> participants) {
private List<MemberDisplay> getMemberDisplays(List<MeetupParticipant> participants) {
return participants
.stream()
.map(meetupParticipator -> MemberDisplay.fromAUserInAServer(meetupParticipator.getParticipator()))
@@ -130,11 +166,16 @@ public class MeetupServiceBean {
return templateService.renderEmbedTemplate(MEETUP_DISPLAY_TEMPLATE, model);
}
public CompletableFuture<Void> cancelMeetup(Meetup meetup, List<String> componentPayloads) {
public CompletableFuture<Void> cancelMeetup(Meetup meetup) {
Long serverId = meetup.getServer().getId();
Long meetupId = meetup.getId().getId();
GuildMessageChannel channel = channelService.getMessageChannelFromServer(serverId, meetup.getMeetupChannel().getId());
MeetupMessageModel model = getMeetupMessageModel(meetup);
List<String> componentPayloads = meetup
.getMeetupComponents()
.stream()
.map(meetupComponent -> meetupComponent.getId().getComponentId())
.collect(Collectors.toList());
model.setCancelled(true);
MessageToSend meetupMessage = getMeetupMessage(model);
return messageService.editMessageInChannel(channel, meetupMessage, meetup.getMessageId())
@@ -157,7 +198,11 @@ public class MeetupServiceBean {
Long userId = meetupParticipator.getParticipator().getUserReference().getId();
userService.retrieveUserForId(userId)
.thenCompose(user -> messageService.sendMessageToSendToUser(user, messageToSend))
.thenAccept(message -> log.info("Notified user {} about cancellation of meetup {} in server {}.", userId, meetupId, serverId));
.thenAccept(message -> log.info("Notified user {} about cancellation of meetup {} in server {}.", userId, meetupId, serverId))
.exceptionally(throwable -> {
log.warn("Failed to notify user {} about cancellation of meetup {} in server {}.", userId, meetupId, serverId);
return null;
});
});
}
@@ -165,6 +210,7 @@ public class MeetupServiceBean {
public void cleanupMeetup(Long meetupId, Long serverId, List<String> componentPayloads) {
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, serverId);
meetup.setState(MeetupState.CANCELLED);
meetupComponentManagementServiceBean.deleteAllComponents(meetup);
log.info("Cleanup meetup {} in server {}.", meetup, serverId);
if(meetup.getEarlyReminderJobTriggerKey() != null) {
schedulerService.stopTrigger(meetup.getEarlyReminderJobTriggerKey());
@@ -203,6 +249,25 @@ public class MeetupServiceBean {
}
}
public CompletableFuture<Void> notifyMeetupParticipants(Meetup meetup, String message) {
List<MeetupDecision> decisionsToBeNotified = Arrays.asList(MeetupDecision.MAYBE, MeetupDecision.YES);
List<MemberDisplay> participants = meetup
.getParticipants()
.stream()
.filter(meetupParticipator -> decisionsToBeNotified.contains(meetupParticipator.getDecision()))
.map(meetupParticipator -> MemberDisplay.fromAUserInAServer(meetupParticipator.getParticipator()))
.collect(Collectors.toList());
MeetupNotificationModel model = MeetupNotificationModel
.builder()
.notificationMessage(message)
.participants(participants)
.build();
MessageChannel channel = channelService.getMessageChannelFromServer(meetup.getServer().getId(), meetup.getMeetupChannel().getId());
MessageToSend messageToSend = templateService.renderEmbedTemplate("notifyMeetupParticipants_notification_message", model, meetup.getServer().getId());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, channel));
}
@Transactional
public void remindParticipants(Long meetupId, Long serverId) {
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, serverId);
@@ -236,7 +301,79 @@ public class MeetupServiceBean {
if(meetup.getMessageId() != null) {
messageService.deleteMessageInChannelInServer(meetup.getServer().getId(), meetup.getMeetupChannel().getId(), meetup.getMessageId());
}
meetupComponentManagementServiceBean.deleteAllComponents(meetup);
});
meetupManagementServiceBean.deleteMeetups(oldMeetups);
}
public CompletableFuture<Void> changeMeetupTimeAndNotifyParticipants(Meetup meetup, Instant newTime) {
List<MeetupDecision> decisions = Arrays.asList(MeetupDecision.MAYBE, MeetupDecision.NO_TIME, MeetupDecision.YES);
List<MeetupParticipant> participants = meetup
.getParticipants()
.stream()
.filter(meetupParticipator -> decisions.contains(meetupParticipator.getDecision()))
.collect(Collectors.toList());
List<Long> userIdsToNotify = participants
.stream()
.map(meetupParticipator -> meetupParticipator.getParticipator().getUserReference().getId())
.collect(Collectors.toList());
Long serverId = meetup.getServer().getId();
ServerChannelMessage meetupMessage = ServerChannelMessage
.builder()
.serverId(serverId)
.channelId(meetup.getMeetupChannel().getId())
.messageId(meetup.getMessageId())
.build();
MeetupTimeChangedNotificationModel notificationModel = MeetupTimeChangedNotificationModel
.builder()
.meetupDescription(meetup.getDescription())
.meetupTopic(meetup.getTopic())
.meetupMessage(meetupMessage)
.newDate(newTime)
.oldDate(meetup.getMeetupTime())
.build();
if(meetup.getEarlyReminderJobTriggerKey() != null) {
schedulerService.stopTrigger(meetup.getEarlyReminderJobTriggerKey());
}
if(meetup.getLateReminderJobTriggerKey() != null) {
schedulerService.stopTrigger(meetup.getLateReminderJobTriggerKey());
}
// set the new time here, so that we can use it in schedule
meetup.setMeetupTime(newTime);
scheduleReminders(meetup);
MessageToSend messageToSend = templateService.renderEmbedTemplate(MEETUP_CHANGE_TIME_NOTIFICATION_TEMPLATE, notificationModel, serverId);
Long meetupId = meetup.getId().getId();
userIdsToNotify.forEach(userId -> {
userService.retrieveUserForId(userId).thenCompose(user -> messageService.sendMessageToSendToUser(user, messageToSend)
.exceptionally(throwable -> {
log.warn("Failed to notify user {} about changed time of meetup {} in server {}.", userId, meetupId, serverId);
return null;
}));
});
meetupParticipatorManagementServiceBean.deleteParticipants(participants);
List<Long> userInServerIds = participants
.stream()
.map(meetupParticipant -> meetupParticipant.getParticipator().getUserInServerId())
.collect(Collectors.toList());
meetup
.getParticipants().removeIf(meetupParticipant -> userInServerIds.contains(meetupParticipant.getParticipator().getUserInServerId()));
MeetupMessageModel meetupMessageModel = getMeetupMessageModel(meetup);
meetupMessageModel.setParticipants(new ArrayList<>());
meetupMessageModel.setMaybeParticipants(new ArrayList<>());
meetupMessageModel.setNoTimeParticipants(new ArrayList<>());
MessageToSend updatedMeetupMessage = getMeetupMessage(meetupMessageModel);
GuildMessageChannel meetupChannel = channelService.getMessageChannelFromServer(serverId, meetup.getMeetupChannel().getId());
return channelService.editEmbedMessageInAChannel(updatedMeetupMessage.getEmbeds().get(0), meetupChannel, meetup.getMessageId())
.thenAccept(message -> log.info("Updated message of meetup {} in channel {} in server {}.", meetupId, meetup.getMeetupChannel().getId(), serverId))
.exceptionally(throwable -> {
log.info("Failed to update message of meetup {} in channel {} in server {}.", meetupId, meetup.getMeetupChannel().getId(), serverId, throwable);
return null;
});
}
}

View File

@@ -0,0 +1,40 @@
package dev.sheldan.sissi.module.meetup.service.management;
import dev.sheldan.abstracto.core.models.database.ComponentPayload;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
import dev.sheldan.sissi.module.meetup.model.database.MeetupComponent;
import dev.sheldan.sissi.module.meetup.model.database.embed.MeetupComponentId;
import dev.sheldan.sissi.module.meetup.repository.MeetupComponentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MeetupComponentManagementServiceBean {
@Autowired
private MeetupComponentRepository meetupComponentRepository;
public MeetupComponent createComponent(Meetup meetup, String componentId, ComponentPayload payload) {
MeetupComponentId id = MeetupComponentId
.builder()
.meetupId(meetup.getId().getId())
.serverId(meetup.getServer().getId())
.componentId(componentId)
.build();
MeetupComponent component = MeetupComponent
.builder()
.id(id)
.payload(payload)
.meetup(meetup)
.build();
return meetupComponentRepository.save(component);
}
public void deleteComponent(MeetupComponent meetupComponent) {
meetupComponentRepository.delete(meetupComponent);
}
public void deleteAllComponents(Meetup meetup) {
meetupComponentRepository.deleteAll(meetup.getMeetupComponents());
}
}

View File

@@ -3,21 +3,24 @@ package dev.sheldan.sissi.module.meetup.service.management;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
import dev.sheldan.sissi.module.meetup.model.database.MeetupDecision;
import dev.sheldan.sissi.module.meetup.model.database.MeetupParticipator;
import dev.sheldan.sissi.module.meetup.model.database.MeetupParticipant;
import dev.sheldan.sissi.module.meetup.model.database.embed.MeetupParticipationId;
import dev.sheldan.sissi.module.meetup.repository.MeetupParticipatorRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
@Slf4j
public class MeetupParticipatorManagementServiceBean {
@Autowired
private MeetupParticipatorRepository repository;
public Optional<MeetupParticipator> getParticipation(Meetup meetup, AUserInAServer aUserInAServer) {
public Optional<MeetupParticipant> getParticipation(Meetup meetup, AUserInAServer aUserInAServer) {
MeetupParticipationId id = MeetupParticipationId
.builder()
.meetupId(meetup.getId().getId())
@@ -27,14 +30,20 @@ public class MeetupParticipatorManagementServiceBean {
return repository.findById(id);
}
public MeetupParticipator createParticipation(Meetup meetup, AUserInAServer aUserInAServer, MeetupDecision meetupDecision) {
public void deleteParticipants(List<MeetupParticipant> participants) {
log.info("Deleting {} participants", participants.size());
repository.deleteAll(participants);
participants.forEach(meetupParticipant -> meetupParticipant.setMeetup(null));
}
public MeetupParticipant createParticipation(Meetup meetup, AUserInAServer aUserInAServer, MeetupDecision meetupDecision) {
MeetupParticipationId id = MeetupParticipationId
.builder()
.meetupId(meetup.getId().getId())
.serverId(meetup.getServer().getId())
.participatorId(aUserInAServer.getUserInServerId())
.build();
MeetupParticipator participator = MeetupParticipator
MeetupParticipant participator = MeetupParticipant
.builder()
.id(id)
.meetup(meetup)

View File

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

View File

@@ -0,0 +1,30 @@
<?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="meetupFeature" value="(SELECT id FROM feature WHERE key = 'meetup')"/>
<changeSet author="Sheldan" id="meetup-commands">
<insert tableName="command">
<column name="name" value="cancelMeetup"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${meetupFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="changeMeetupTime"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${meetupFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="notifyMeetupParticipants"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${meetupFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

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

View File

@@ -0,0 +1,37 @@
<?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="meetup_component-table">
<createTable tableName="meetup_component">
<column name="component_id" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
<column name="meetup_id" type="BIGINT">
<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>
</createTable>
<addPrimaryKey columnNames="component_id, meetup_id, server_id" tableName="meetup_component" constraintName="pk_meetup_component" validate="true"/>
<addForeignKeyConstraint baseColumnNames="meetup_id, server_id" baseTableName="meetup_component" constraintName="fk_meetup_component_meetup"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id, server_id" referencedTableName="meetup" validate="true"/>
<addForeignKeyConstraint baseColumnNames="component_id" baseTableName="meetup_component" constraintName="fk_meetup_component_component"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
referencedColumnNames="id" referencedTableName="component_payload" validate="false"/>
<sql>
DROP TRIGGER IF EXISTS meetup_component_insert_trigger ON meetup_component;
CREATE TRIGGER meetup_component_insert_trigger BEFORE INSERT ON meetup_component FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,16 @@
<?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="meetup_participator-table">
<sql>
ALTER TABLE meetup_participator DROP CONSTRAINT check_meetup_participator_decision;
ALTER TABLE meetup_participator ADD CONSTRAINT check_meetup_participator_decision CHECK (decision IN ('YES','NO', 'MAYBE', 'NO_TIME'));
</sql>
</changeSet>
</databaseChangeLog>

View File

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

View File

@@ -7,4 +7,5 @@
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="1.1.0/collection.xml" relativeToChangelogFile="true"/>
<include file="1.2.0/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>