[AB-333] providing some dates as separate instants instead of relying on the member attributes

truncating date to day when displaying the date for export emote stats
fixing wrong message when confirming a command and adding missing exception handling
This commit is contained in:
Sheldan
2021-10-14 01:31:52 +02:00
parent 36ca9b11e4
commit 8909e8ebe5
10 changed files with 49 additions and 31 deletions

View File

@@ -313,13 +313,16 @@ public class MuteServiceBean implements MuteService {
finalFuture.complete(null) finalFuture.complete(null)
).exceptionally(throwable1 -> { ).exceptionally(throwable1 -> {
log.error("Unmute log failed to send for mute {} in server {}.", muteId, serverId, throwable1); log.error("Unmute log failed to send for mute {} in server {}.", muteId, serverId, throwable1);
finalFuture.complete(null); finalFuture.completeExceptionally(null);
return null; return null;
}); });
} else { } else {
finalFuture.complete(null); finalFuture.complete(null);
} }
return null; return null;
}).exceptionally(throwable -> {
finalFuture.completeExceptionally(throwable);
return null;
}); });
return finalFuture; return finalFuture;

View File

@@ -269,6 +269,9 @@ public class WarnServiceBean implements WarnService {
return null; return null;
}); });
return null; return null;
}).exceptionally(throwable -> {
sendingFuture.completeExceptionally(throwable);
return null;
}); });
return sendingFuture; return sendingFuture;

View File

@@ -425,6 +425,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
.builder() .builder()
.member(member) .member(member)
.latestModMailThread(latestThread) .latestModMailThread(latestThread)
.memberJoinDate(member.getTimeJoined().toInstant())
.pastModMailThreadCount((long)oldThreads.size()) .pastModMailThreadCount((long)oldThreads.size())
.build(); .build();
List<CompletableFuture<Message>> messages = channelService.sendEmbedTemplateInTextChannelList("modmail_thread_header", header, channel); List<CompletableFuture<Message>> messages = channelService.sendEmbedTemplateInTextChannelList("modmail_thread_header", header, channel);

View File

@@ -6,6 +6,8 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import java.time.Instant;
/** /**
* This is the model used when a new mod mail thread is opened and a message containing some information about the user * This is the model used when a new mod mail thread is opened and a message containing some information about the user
* is displayed for the user handling the mod mail thread. * is displayed for the user handling the mod mail thread.
@@ -23,6 +25,7 @@ public class ModMailThreaderHeader {
* for the user * for the user
*/ */
private ModMailThread latestModMailThread; private ModMailThread latestModMailThread;
private Instant memberJoinDate;
/** /**
* The amount of previous mod mail thread the user has. * The amount of previous mod mail thread the user has.
*/ */

View File

@@ -29,6 +29,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -71,7 +72,7 @@ public class ExportEmoteStats extends AbstractConditionableCommand {
if(!parameters.isEmpty()) { if(!parameters.isEmpty()) {
// if a duration is given, subtract this duration from the current point in time // if a duration is given, subtract this duration from the current point in time
Duration duration = (Duration) parameters.get(0); Duration duration = (Duration) parameters.get(0);
statsSince = Instant.now().minus(duration); statsSince = Instant.now().minus(duration).truncatedTo(ChronoUnit.DAYS);
} }
AServer actualServer = serverManagementService.loadServer(commandContext.getGuild().getIdLong()); AServer actualServer = serverManagementService.loadServer(commandContext.getGuild().getIdLong());
List<UsedEmote> usedEmotes = usedEmoteManagementService.loadEmoteUsagesForServerSince(actualServer, statsSince); List<UsedEmote> usedEmotes = usedEmoteManagementService.loadEmoteUsagesForServerSince(actualServer, statsSince);

View File

@@ -50,11 +50,15 @@ public class UserInfo extends AbstractConditionableCommand {
log.info("Force reloading member {} in guild {} for user info.", memberToShow.getId(), memberToShow.getGuild().getId()); log.info("Force reloading member {} in guild {} for user info.", memberToShow.getId(), memberToShow.getGuild().getId());
return memberService.forceReloadMember(memberToShow).thenCompose(member -> { return memberService.forceReloadMember(memberToShow).thenCompose(member -> {
model.setMemberInfo(member); model.setMemberInfo(member);
model.setCreationDate(member.getTimeCreated().toInstant());
model.setJoinDate(member.getTimeJoined().toInstant());
return self.sendResponse(commandContext, model) return self.sendResponse(commandContext, model)
.thenApply(aVoid -> CommandResult.fromIgnored()); .thenApply(aVoid -> CommandResult.fromIgnored());
}); });
} else { } else {
model.setMemberInfo(memberToShow); model.setMemberInfo(memberToShow);
model.setCreationDate(memberToShow.getTimeCreated().toInstant());
model.setJoinDate(memberToShow.getTimeJoined().toInstant());
return self.sendResponse(commandContext, model) return self.sendResponse(commandContext, model)
.thenApply(aVoid -> CommandResult.fromIgnored()); .thenApply(aVoid -> CommandResult.fromIgnored());
} }

View File

@@ -15,6 +15,8 @@ import org.junit.runner.RunWith;
import org.mockito.*; import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -43,6 +45,8 @@ public class UserInfoTest {
CommandContext noParameters = CommandTestUtilities.getNoParameters(); CommandContext noParameters = CommandTestUtilities.getNoParameters();
when(noParameters.getAuthor().getGuild()).thenReturn(noParameters.getGuild()); when(noParameters.getAuthor().getGuild()).thenReturn(noParameters.getGuild());
when(noParameters.getAuthor().hasTimeJoined()).thenReturn(true); when(noParameters.getAuthor().hasTimeJoined()).thenReturn(true);
when(noParameters.getAuthor().getTimeJoined()).thenReturn(OffsetDateTime.now());
when(noParameters.getAuthor().getTimeCreated()).thenReturn(OffsetDateTime.now());
when(self.sendResponse(eq(noParameters),any(UserInfoModel.class))).thenReturn(CompletableFuture.completedFuture(null)); when(self.sendResponse(eq(noParameters),any(UserInfoModel.class))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(noParameters); CompletableFuture<CommandResult> result = testUnit.executeAsync(noParameters);
verify(self, times(1)).sendResponse(eq(noParameters), modelArgumentCaptor.capture()); verify(self, times(1)).sendResponse(eq(noParameters), modelArgumentCaptor.capture());
@@ -56,6 +60,8 @@ public class UserInfoTest {
CommandContext noParameters = CommandTestUtilities.getNoParameters(); CommandContext noParameters = CommandTestUtilities.getNoParameters();
when(noParameters.getAuthor().hasTimeJoined()).thenReturn(false); when(noParameters.getAuthor().hasTimeJoined()).thenReturn(false);
Member loadedAuthor = Mockito.mock(Member.class); Member loadedAuthor = Mockito.mock(Member.class);
when(loadedAuthor.getTimeJoined()).thenReturn(OffsetDateTime.now());
when(loadedAuthor.getTimeCreated()).thenReturn(OffsetDateTime.now());
when(noParameters.getAuthor().getGuild()).thenReturn(noParameters.getGuild()); when(noParameters.getAuthor().getGuild()).thenReturn(noParameters.getGuild());
when(memberService.forceReloadMember(noParameters.getAuthor())).thenReturn(CompletableFuture.completedFuture(loadedAuthor)); when(memberService.forceReloadMember(noParameters.getAuthor())).thenReturn(CompletableFuture.completedFuture(loadedAuthor));
when(self.sendResponse(eq(noParameters), modelArgumentCaptor.capture())).thenReturn(CompletableFuture.completedFuture(null)); when(self.sendResponse(eq(noParameters), modelArgumentCaptor.capture())).thenReturn(CompletableFuture.completedFuture(null));
@@ -69,6 +75,8 @@ public class UserInfoTest {
public void executeTestWithParameterLoadedMember() { public void executeTestWithParameterLoadedMember() {
Member member = Mockito.mock(Member.class); Member member = Mockito.mock(Member.class);
when(member.hasTimeJoined()).thenReturn(true); when(member.hasTimeJoined()).thenReturn(true);
when(member.getTimeJoined()).thenReturn(OffsetDateTime.now());
when(member.getTimeCreated()).thenReturn(OffsetDateTime.now());
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(member)); CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(member));
when(member.getGuild()).thenReturn(parameters.getGuild()); when(member.getGuild()).thenReturn(parameters.getGuild());
when(self.sendResponse(eq(parameters), modelArgumentCaptor.capture())).thenReturn(CompletableFuture.completedFuture(null)); when(self.sendResponse(eq(parameters), modelArgumentCaptor.capture())).thenReturn(CompletableFuture.completedFuture(null));
@@ -85,6 +93,8 @@ public class UserInfoTest {
CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(member)); CommandContext parameters = CommandTestUtilities.getWithParameters(Arrays.asList(member));
when(member.getGuild()).thenReturn(parameters.getGuild()); when(member.getGuild()).thenReturn(parameters.getGuild());
Member loadedAuthor = Mockito.mock(Member.class); Member loadedAuthor = Mockito.mock(Member.class);
when(loadedAuthor.getTimeJoined()).thenReturn(OffsetDateTime.now());
when(loadedAuthor.getTimeCreated()).thenReturn(OffsetDateTime.now());
when(memberService.forceReloadMember(member)).thenReturn(CompletableFuture.completedFuture(loadedAuthor)); when(memberService.forceReloadMember(member)).thenReturn(CompletableFuture.completedFuture(loadedAuthor));
when(self.sendResponse(eq(parameters), modelArgumentCaptor.capture())).thenReturn(CompletableFuture.completedFuture(null)); when(self.sendResponse(eq(parameters), modelArgumentCaptor.capture())).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters); CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);

View File

@@ -6,9 +6,13 @@ import lombok.Setter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import java.time.Instant;
@Getter @Getter
@Setter @Setter
@SuperBuilder @SuperBuilder
public class UserInfoModel extends SlimUserInitiatedServerContext { public class UserInfoModel extends SlimUserInitiatedServerContext {
private Member memberInfo; private Member memberInfo;
private Instant joinDate;
private Instant creationDate;
} }

View File

@@ -271,13 +271,13 @@ public class CommandReceivedHandler extends ListenerAdapter {
List<CompletableFuture<Message>> confirmationMessageFutures = channelService.sendMessageToSendToChannel(message, event.getChannel()); List<CompletableFuture<Message>> confirmationMessageFutures = channelService.sendMessageToSendToChannel(message, event.getChannel());
FutureUtils.toSingleFutureGeneric(confirmationMessageFutures) FutureUtils.toSingleFutureGeneric(confirmationMessageFutures)
.thenAccept(unused -> self.persistConfirmationCallbacks(model, confirmationMessageFutures.get(0).join())) .thenAccept(unused -> self.persistConfirmationCallbacks(model, confirmationMessageFutures.get(0).join()))
.exceptionally(throwable -> self.failedCommandHandling(event, foundCommand, parsedParameters, commandContext, throwable)); .exceptionally(throwable -> self.handleFailedCommand(foundCommand, commandContext, throwable));
} else if(commandConfiguration.isAsync()) { } else if(commandConfiguration.isAsync()) {
log.info("Executing async command {} for server {} in channel {} based on message {} by user {}.", log.info("Executing async command {} for server {} in channel {} based on message {} by user {}.",
commandConfiguration.getName(), commandContext.getGuild().getId(), commandContext.getChannel().getId(), commandContext.getMessage().getId(), commandContext.getAuthor().getId()); commandConfiguration.getName(), commandContext.getGuild().getId(), commandContext.getChannel().getId(), commandContext.getMessage().getId(), commandContext.getAuthor().getId());
self.executeAsyncCommand(foundCommand, commandContext) self.executeAsyncCommand(foundCommand, commandContext)
.exceptionally(throwable -> failedCommandHandling(event, foundCommand, parsedParameters, commandContext, throwable)); .exceptionally(throwable -> handleFailedCommand(foundCommand, commandContext, throwable));
} else { } else {
commandResult = self.executeCommand(foundCommand, commandContext); commandResult = self.executeCommand(foundCommand, commandContext);
} }
@@ -287,32 +287,25 @@ public class CommandReceivedHandler extends ListenerAdapter {
if(commandResult != null) { if(commandResult != null) {
self.executePostCommandListener(foundCommand, commandContext, commandResult); self.executePostCommandListener(foundCommand, commandContext, commandResult);
} }
}).exceptionally(throwable -> failedCommandHandling(event, foundCommand, parsedParameters, commandContext, throwable)); }).exceptionally(throwable -> handleFailedCommand(foundCommand, commandContext, throwable));
} }
private Void failedCommandHandling(MessageReceivedEvent event, Command foundCommand, Parameters parsedParameters, CommandContext commandContext, Throwable throwable) { private Void handleFailedCommand(Command foundCommand, CommandContext commandContext, Throwable throwable) {
log.error("Asynchronous command {} failed.", foundCommand.getConfiguration().getName(), throwable); log.error("Asynchronous command {} failed.", foundCommand.getConfiguration().getName(), throwable);
UserInitiatedServerContext rebuildUserContext = buildUserInitiatedServerContext(commandContext);
CommandContext rebuildContext = CommandContext.builder()
.author(event.getMember())
.guild(event.getGuild())
.channel(event.getTextChannel())
.message(event.getMessage())
.jda(event.getJDA())
.undoActions(commandContext.getUndoActions()) // TODO really do this? it would need to guarantee that its available and usable
.userInitiatedContext(rebuildUserContext)
.parameters(parsedParameters).build();
CommandResult failedResult = CommandResult.fromError(throwable.getMessage(), throwable); CommandResult failedResult = CommandResult.fromError(throwable.getMessage(), throwable);
self.executePostCommandListener(foundCommand, rebuildContext, failedResult); self.executePostCommandListener(foundCommand, commandContext, failedResult);
return null; return null;
} }
@Transactional(isolation = Isolation.SERIALIZABLE) @Transactional(isolation = Isolation.SERIALIZABLE)
public CompletableFuture<Void> executeAsyncCommand(Command foundCommand, CommandContext commandContext) { public CompletableFuture<Void> executeAsyncCommand(Command foundCommand, CommandContext commandContext) {
return foundCommand.executeAsync(commandContext).thenAccept(result -> return foundCommand.executeAsync(commandContext).thenAccept(result ->
executePostCommandListener(foundCommand, commandContext, result) executePostCommandListener(foundCommand, commandContext, result)
); ).exceptionally(throwable -> {
handleFailedCommand(foundCommand, commandContext, throwable);
return null;
});
} }
@Transactional @Transactional
@@ -391,10 +384,6 @@ public class CommandReceivedHandler extends ListenerAdapter {
return buildUserInitiatedServerContext(event.getMember(), event.getTextChannel(), event.getGuild()); return buildUserInitiatedServerContext(event.getMember(), event.getTextChannel(), event.getGuild());
} }
private UserInitiatedServerContext buildUserInitiatedServerContext(CommandContext context) {
return buildUserInitiatedServerContext(context.getAuthor(), context.getChannel(), context.getGuild());
}
public CompletableFuture<Parameters> getParsedParameters(UnParsedCommandParameter unParsedCommandParameter, Command command, Message message){ public CompletableFuture<Parameters> getParsedParameters(UnParsedCommandParameter unParsedCommandParameter, Command command, Message message){
List<ParseResult> parsedParameters = new ArrayList<>(); List<ParseResult> parsedParameters = new ArrayList<>();
List<Parameter> parameters = command.getConfiguration().getParameters(); List<Parameter> parameters = command.getConfiguration().getParameters();

View File

@@ -63,7 +63,12 @@ public class ConfirmationButtonClickedListener implements ButtonClickedListener
log.info("Denying command {} in server {} from message {} in channel {} with event {}.", log.info("Denying command {} in server {} from message {} in channel {} with event {}.",
commandCtx.getCommandName(), commandCtx.getServerId(), commandCtx.getMessageId(), commandCtx.getCommandName(), commandCtx.getServerId(), commandCtx.getMessageId(),
commandCtx.getChannelId(), model.getEvent().getInteraction().getId()); commandCtx.getChannelId(), model.getEvent().getInteraction().getId());
cleanup(model, payload); cleanup(model, payload)
.thenAccept(unused -> self.sendAbortNotification(model))
.exceptionally(throwable -> {
log.warn("Failed to clean up confirmation message {}.", model.getEvent().getMessageId());
return null;
});
} }
return ButtonClickedListenerResult.ACKNOWLEDGED; return ButtonClickedListenerResult.ACKNOWLEDGED;
} }
@@ -84,16 +89,11 @@ public class ConfirmationButtonClickedListener implements ButtonClickedListener
} }
} }
private void cleanup(ButtonClickedListenerModel model, CommandConfirmationPayload payload) { private CompletableFuture<Void> cleanup(ButtonClickedListenerModel model, CommandConfirmationPayload payload) {
log.debug("Cleaning up component {} and {}.", payload.getOtherButtonComponentId(), model.getEvent().getComponentId()); log.debug("Cleaning up component {} and {}.", payload.getOtherButtonComponentId(), model.getEvent().getComponentId());
componentPayloadManagementService.deletePayloads(Arrays.asList(payload.getOtherButtonComponentId(), model.getEvent().getComponentId())); componentPayloadManagementService.deletePayloads(Arrays.asList(payload.getOtherButtonComponentId(), model.getEvent().getComponentId()));
log.debug("Deleting confirmation message {}.", model.getEvent().getMessageId()); log.debug("Deleting confirmation message {}.", model.getEvent().getMessageId());
messageService.deleteMessage(model.getEvent().getMessage()) return messageService.deleteMessage(model.getEvent().getMessage());
.thenAccept(unused -> self.sendAbortNotification(model))
.exceptionally(throwable -> {
log.warn("Failed to clean up confirmation message {}.", model.getEvent().getMessageId());
return null;
});
} }
public CompletableFuture<Void> sendAbortNotification(ButtonClickedListenerModel model) { public CompletableFuture<Void> sendAbortNotification(ButtonClickedListenerModel model) {