diff --git a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java index 1a2a0ad96..f4a9c192e 100644 --- a/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java +++ b/abstracto-application/abstracto-modules/experience-tracking/experience-tracking-impl/src/main/java/dev/sheldan/abstracto/experience/service/AUserExperienceServiceBean.java @@ -314,9 +314,9 @@ public class AUserExperienceServiceBean implements AUserExperienceService { AServer server = serverManagementService.loadOrCreate(serverId); List disabledExpRoles = disabledExpRoleManagementService.getDisabledRolesForServer(server); List disabledRoles = disabledExpRoles - .stream() - .map(ADisabledExpRole::getRole) - .collect(Collectors.toList()); + .stream() + .map(ADisabledExpRole::getRole) + .collect(Collectors.toList()); if (roleService.hasAnyOfTheRoles(member, disabledRoles)) { log.debug("User {} has a experience disable role in server {} - not giving any experience.", member.getIdLong(), serverId); newSpan.end(); @@ -342,29 +342,31 @@ public class AUserExperienceServiceBean implements AUserExperienceService { log.debug("Handling {}. The user gains {}.", userInServerId, gainedExperience); - Long oldExperience = aUserExperience.getExperience(); - Long newExperienceCount = oldExperience + gainedExperience; - aUserExperience.setExperience(newExperienceCount); - AExperienceLevel newLevel = calculateLevel(levels, newExperienceCount); - RoleCalculationResult result = RoleCalculationResult + Long oldExperience = aUserExperience.getExperience(); + Long newExperienceCount = oldExperience + gainedExperience; + aUserExperience.setExperience(newExperienceCount); + AExperienceLevel newLevel = calculateLevel(levels, newExperienceCount); + RoleCalculationResult result = RoleCalculationResult .builder() .build(); - boolean userChangesLevel = !Objects.equals(newLevel.getLevel(), aUserExperience.getCurrentLevel().getLevel()); - Integer oldLevel = aUserExperience.getCurrentLevel() != null ? aUserExperience.getCurrentLevel().getLevel() : 0; - if(userChangesLevel) { - log.info("User {} in server {} changed level. New {}, Old {}.", member.getIdLong(), + boolean userChangesLevel = !Objects.equals(newLevel.getLevel(), aUserExperience.getCurrentLevel().getLevel()); + Integer oldLevel = aUserExperience.getCurrentLevel() != null ? aUserExperience.getCurrentLevel().getLevel() : 0; + if (userChangesLevel) { + log.info("User {} in server {} changed level. New {}, Old {}.", member.getIdLong(), member.getGuild().getIdLong(), newLevel.getLevel(), oldLevel); - aUserExperience.setCurrentLevel(newLevel); - AExperienceRole calculatedNewRole = experienceRoleService.calculateRole(roles, newLevel.getLevel()); - Long oldRoleId = aUserExperience.getCurrentExperienceRole() != null && aUserExperience.getCurrentExperienceRole().getRole() != null ? aUserExperience.getCurrentExperienceRole().getRole().getId() : null; - Long newRoleId = calculatedNewRole != null && calculatedNewRole.getRole() != null ? calculatedNewRole.getRole().getId() : null; - result.setOldRoleId(oldRoleId); - result.setNewRoleId(newRoleId); - if(message != null + aUserExperience.setCurrentLevel(newLevel); + AExperienceRole calculatedNewRole = experienceRoleService.calculateRole(roles, newLevel.getLevel()); + Long oldRoleId = aUserExperience.getCurrentExperienceRole() != null && aUserExperience.getCurrentExperienceRole().getRole() != null ? + aUserExperience.getCurrentExperienceRole().getRole().getId() : null; + Long newRoleId = calculatedNewRole != null && calculatedNewRole.getRole() != null ? calculatedNewRole.getRole().getId() : null; + result.setOldRoleId(oldRoleId); + result.setNewRoleId(newRoleId); + if (message != null && aUserExperience.getLevelUpNotification() - && featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, serverId, ExperienceFeatureMode.LEVEL_UP_NOTIFICATION)) { - LevelUpNotificationModel model = LevelUpNotificationModel + && + featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, serverId, ExperienceFeatureMode.LEVEL_UP_NOTIFICATION)) { + LevelUpNotificationModel model = LevelUpNotificationModel .builder() .memberDisplay(MemberDisplay.fromMember(member)) .oldExperience(oldExperience) @@ -374,63 +376,72 @@ public class AUserExperienceServiceBean implements AUserExperienceService { .newRole(oldRoleId != null ? RoleDisplay.fromRole(oldRoleId) : null) .newRole(newRoleId != null ? RoleDisplay.fromRole(newRoleId) : null) .build(); - MessageToSend messageToSend = templateService.renderEmbedTemplate("experience_level_up_notification", model, serverId); - FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, message.getChannel())).thenAccept(unused -> { - log.info("Sent level up notification to user {} in server {} in channel {}.", member.getIdLong(), serverId, message.getChannel().getIdLong()); - }).exceptionally(throwable -> { - log.warn("Failed to send level up notification to user {} in server {} in channel {}.", member.getIdLong(), serverId, message.getChannel().getIdLong()); - return null; - }); - } - aUserExperience.setCurrentExperienceRole(calculatedNewRole); - } - aUserExperience.setMessageCount(aUserExperience.getMessageCount() + 1L); - if(userChangesLevel && featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, server, ExperienceFeatureMode.LEVEL_ACTION)) { - levelActionService.applyLevelActionsToUser(aUserExperience, oldLevel) - .thenAccept(unused -> { - log.info("Executed level actions for user {}.", userInServerId); - }) - .exceptionally(throwable -> { - log.warn("Failed to execute level actions for user {}.", userInServerId, throwable); - return null; - }); - } - if(aUserExperienceOptional.isEmpty()) { - userExperienceManagementService.saveUser(aUserExperience); - } - if(!Objects.equals(result.getOldRoleId(), result.getNewRoleId())) { - if(result.getOldRoleId() != null && result.getNewRoleId() != null) { - roleService.updateRolesIds(member, Arrays.asList(result.getOldRoleId()), Arrays.asList(result.getNewRoleId())).thenAccept(unused -> { - log.debug("Removed role {} from and added role {} to member {} in server {}.", result.getOldRoleId(), result.getNewRoleId(), member.getIdLong(), member.getGuild().getIdLong()); - }).exceptionally(throwable -> { - log.warn("Failed to remove role {} from and add role {} to member {} in server {}.", result.getOldRoleId(), result.getNewRoleId(), member.getIdLong(), member.getGuild().getIdLong(), throwable); - return null; - }); - } else { - if(result.getOldRoleId() != null) { - roleService.removeRoleFromMemberAsync(member, result.getOldRoleId()).thenAccept(unused -> { - log.debug("Removed role {} from member {} in server {}.", result.getOldRoleId(), member.getIdLong(), member.getGuild().getIdLong()); + MessageToSend messageToSend = templateService.renderEmbedTemplate("experience_level_up_notification", model, serverId); + FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, message.getChannel())).thenAccept(unused -> { + log.info("Sent level up notification to user {} in server {} in channel {}.", member.getIdLong(), serverId, + message.getChannel().getIdLong()); }).exceptionally(throwable -> { - log.warn("Failed to remove role {} from member {} in server {}.", result.getOldRoleId(), member.getIdLong(), member.getGuild().getIdLong(), throwable); + log.warn("Failed to send level up notification to user {} in server {} in channel {}.", member.getIdLong(), serverId, + message.getChannel().getIdLong()); return null; }); } - if(result.getNewRoleId() != null) { - roleService.addRoleToMemberAsync(member, result.getNewRoleId()).thenAccept(unused -> { - log.debug("Added role {} to member {} in server {}.", result.getNewRoleId(), member.getIdLong(), member.getGuild().getIdLong()); - }).exceptionally(throwable -> { - log.warn("Failed to add role {} to member {} in server {}.", result.getOldRoleId(), member.getIdLong(), member.getGuild().getIdLong(), throwable); + aUserExperience.setCurrentExperienceRole(calculatedNewRole); + } + aUserExperience.setMessageCount(aUserExperience.getMessageCount() + 1L); + if (userChangesLevel && + featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, server, ExperienceFeatureMode.LEVEL_ACTION)) { + levelActionService.applyLevelActionsToUser(aUserExperience, oldLevel) + .thenAccept(unused -> { + log.info("Executed level actions for user {}.", userInServerId); + }) + .exceptionally(throwable -> { + log.warn("Failed to execute level actions for user {}.", userInServerId, throwable); return null; }); + } + if (aUserExperienceOptional.isEmpty()) { + userExperienceManagementService.saveUser(aUserExperience); + } + if (!Objects.equals(result.getOldRoleId(), result.getNewRoleId())) { + if (result.getOldRoleId() != null && result.getNewRoleId() != null) { + roleService.updateRolesIds(member, Arrays.asList(result.getOldRoleId()), Arrays.asList(result.getNewRoleId())).thenAccept(unused -> { + log.debug("Removed role {} from and added role {} to member {} in server {}.", result.getOldRoleId(), result.getNewRoleId(), + member.getIdLong(), member.getGuild().getIdLong()); + }).exceptionally(throwable -> { + log.warn("Failed to remove role {} from and add role {} to member {} in server {}.", result.getOldRoleId(), result.getNewRoleId(), + member.getIdLong(), member.getGuild().getIdLong(), throwable); + return null; + }); + } else { + if (result.getOldRoleId() != null) { + roleService.removeRoleFromMemberAsync(member, result.getOldRoleId()).thenAccept(unused -> { + log.debug("Removed role {} from member {} in server {}.", result.getOldRoleId(), member.getIdLong(), + member.getGuild().getIdLong()); + }).exceptionally(throwable -> { + log.warn("Failed to remove role {} from member {} in server {}.", result.getOldRoleId(), member.getIdLong(), + member.getGuild().getIdLong(), throwable); + return null; + }); + } + if (result.getNewRoleId() != null) { + roleService.addRoleToMemberAsync(member, result.getNewRoleId()).thenAccept(unused -> { + log.debug("Added role {} to member {} in server {}.", result.getNewRoleId(), member.getIdLong(), member.getGuild().getIdLong()); + }).exceptionally(throwable -> { + log.warn("Failed to add role {} to member {} in server {}.", result.getOldRoleId(), member.getIdLong(), + member.getGuild().getIdLong(), throwable); + return null; + }); + } } } + } else { + log.debug("Experience gain was disabled. User did not gain any experience."); } - } else { - log.debug("Experience gain was disabled. User did not gain any experience."); + return CompletableFuture.allOf(notificationFuture, levelActionFuture, roleFuture).whenComplete((unused, throwable) -> { + newSpan.end(); + }); } - return CompletableFuture.allOf(notificationFuture, levelActionFuture, roleFuture).whenComplete((unused, throwable) -> { - newSpan.end(); - }); } @Override diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/InteractionServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/InteractionServiceBean.java index 980d0bb15..e7652ff5f 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/InteractionServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/InteractionServiceBean.java @@ -266,86 +266,86 @@ public class InteractionServiceBean implements InteractionService { public CompletableFuture replyMessageToSend(MessageToSend messageToSend, IReplyCallback callback) { Span newSpan = tracer.nextSpan().name("send-message-to-interaction"); try (Tracer.SpanInScope ws = this.tracer.withSpan(newSpan.start())) { - ReplyCallbackAction action = null; - if(messageToSend.getUseComponentsV2()) { - action = callback.replyComponents(messageToSend.getComponents()).useComponentsV2(); - } else { - if(messageToSend.getMessages() != null && !messageToSend.getMessages().isEmpty()) { - metricService.incrementCounter(MESSAGE_SEND_METRIC); - action = callback.reply(messageToSend.getMessages().get(0)); - } - if(messageToSend.getEmbeds() != null && !messageToSend.getEmbeds().isEmpty()) { - if(action != null) { - action = action.addEmbeds(messageToSend.getEmbeds().subList(0, Math.min(10, messageToSend.getEmbeds().size()))); - } else { - action = callback.replyEmbeds(messageToSend.getEmbeds()); - } - } - if(messageToSend.hasFilesToSend()) { - List attachedFiles = messageToSend - .getAttachedFiles() - .stream() - .map(AttachedFile::convertToFileUpload) - .collect(Collectors.toList()); - if(action != null) { - action.setFiles(attachedFiles); - } else { + ReplyCallbackAction action = null; + if (messageToSend.getUseComponentsV2()) { + action = callback.replyComponents(messageToSend.getComponents()).useComponentsV2(); + } else { + if (messageToSend.getMessages() != null && !messageToSend.getMessages().isEmpty()) { metricService.incrementCounter(MESSAGE_SEND_METRIC); - action = callback.replyFiles(attachedFiles); + action = callback.reply(messageToSend.getMessages().get(0)); } - } - // this should be last, because we are "faking" a message, by inserting a ., in case there has not been a reply yet - // we could also throw an exception, but we are allowing this to go through - List actionRows = messageToSend.getActionRows(); - if(actionRows != null && !actionRows.isEmpty()) { - if(action == null) { - action = callback.reply("."); - } - action = action.setComponents(actionRows); - AServer server; - if(ContextUtils.isGuildKnown(callback)) { - server = serverManagementService.loadServer(callback.getGuild().getIdLong()); - } else { - server = null; - } - actionRows.forEach(components -> components.forEach(component -> { - if(component instanceof ActionComponent) { - String id = ((ActionComponent)component).getId(); - MessageToSend.ComponentConfig payload = messageToSend.getComponentPayloads().get(id); - if(payload != null && payload.getPersistCallback()) { - componentPayloadManagementService.createPayload(id, payload.getPayload(), payload.getPayloadType(), payload.getComponentOrigin(), server, payload.getComponentType()); - } + if (messageToSend.getEmbeds() != null && !messageToSend.getEmbeds().isEmpty()) { + if (action != null) { + action = action.addEmbeds(messageToSend.getEmbeds().subList(0, Math.min(10, messageToSend.getEmbeds().size()))); + } else { + action = callback.replyEmbeds(messageToSend.getEmbeds()); } - })); + } + if (messageToSend.hasFilesToSend()) { + List attachedFiles = messageToSend + .getAttachedFiles() + .stream() + .map(AttachedFile::convertToFileUpload) + .collect(Collectors.toList()); + if (action != null) { + action.setFiles(attachedFiles); + } else { + metricService.incrementCounter(MESSAGE_SEND_METRIC); + action = callback.replyFiles(attachedFiles); + } + } + // this should be last, because we are "faking" a message, by inserting a ., in case there has not been a reply yet + // we could also throw an exception, but we are allowing this to go through + List actionRows = messageToSend.getActionRows(); + if (actionRows != null && !actionRows.isEmpty()) { + if (action == null) { + action = callback.reply("."); + } + action = action.setComponents(actionRows); + AServer server; + if (ContextUtils.isGuildKnown(callback)) { + server = serverManagementService.loadServer(callback.getGuild().getIdLong()); + } else { + server = null; + } + actionRows.forEach(components -> components.forEach(component -> { + if (component instanceof ActionComponent) { + String id = ((ActionComponent) component).getId(); + MessageToSend.ComponentConfig payload = messageToSend.getComponentPayloads().get(id); + if (payload != null && payload.getPersistCallback()) { + componentPayloadManagementService.createPayload(id, payload.getPayload(), payload.getPayloadType(), + payload.getComponentOrigin(), server, payload.getComponentType()); + } + } + })); + } } - } - if(messageToSend.getEphemeral()) { - if(ContextUtils.hasGuild(callback)) { - log.info("Sending ephemeral message to interaction in guild {} in channel {} for user {}.", + if (messageToSend.getEphemeral()) { + if (ContextUtils.hasGuild(callback)) { + log.info("Sending ephemeral message to interaction in guild {} in channel {} for user {}.", callback.getGuild().getIdLong(), callback.getChannel().getId(), callback.getUser().getIdLong()); - } else { - log.info("Sending ephemeral message to user {}.", callback.getUser().getIdLong()); + } else { + log.info("Sending ephemeral message to user {}.", callback.getUser().getIdLong()); + } + metricService.incrementCounter(EPHEMERAL_MESSAGES_SEND); + if (action != null) { + action = action.setEphemeral(messageToSend.getEphemeral()); + } } - metricService.incrementCounter(EPHEMERAL_MESSAGES_SEND); - if(action != null) { - action = action.setEphemeral(messageToSend.getEphemeral()); + if (ContextUtils.isGuildKnown(callback)) { + Set allowedMentions = allowedMentionService.getAllowedMentionsFor(callback.getMessageChannel(), messageToSend); + if (action != null) { + action = action.setAllowedMentions(allowedMentions); + } } - } - if(ContextUtils.isGuildKnown(callback)) { - Set allowedMentions = allowedMentionService.getAllowedMentionsFor(callback.getMessageChannel(), messageToSend); - if (action != null) { - action = action.setAllowedMentions(allowedMentions); - } - } if (action == null) { throw new AbstractoRunTimeException("The callback did not result in any message."); } return action.submit(); } - } } @Override diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandListenerBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandListenerBean.java index cddb8434c..fd0346eaf 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandListenerBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandListenerBean.java @@ -19,23 +19,31 @@ import dev.sheldan.abstracto.core.interaction.slash.payload.SlashCommandConfirma import dev.sheldan.abstracto.core.metric.service.CounterMetric; import dev.sheldan.abstracto.core.metric.service.MetricService; import dev.sheldan.abstracto.core.metric.service.MetricTag; +import dev.sheldan.abstracto.core.metric.service.MetricUtils; import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.service.ConfigService; import dev.sheldan.abstracto.core.service.management.ServerManagementService; +import dev.sheldan.abstracto.core.utils.ContextUtils; import dev.sheldan.abstracto.scheduling.model.JobParameters; import dev.sheldan.abstracto.scheduling.service.SchedulerService; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import dev.sheldan.abstracto.core.utils.ContextUtils; -import dev.sheldan.abstracto.core.metric.service.MetricUtils; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationRegistry; import io.micrometer.tracing.Span; import io.micrometer.tracing.Tracer; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.PostConstruct; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; @@ -50,15 +58,6 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; - @Component @Slf4j public class SlashCommandListenerBean extends ListenerAdapter { @@ -118,46 +117,49 @@ public class SlashCommandListenerBean extends ListenerAdapter { public static final String SLASH_COMMAND_CONFIRMATION_ORIGIN = "SLASH_COMMAND_CONFIRMATION"; public static final CounterMetric SLASH_COMMANDS_PROCESSED_COUNTER = CounterMetric - .builder() - .name(CommandReceivedHandler.COMMAND_PROCESSED) - .tagList(Arrays.asList(MetricTag.getTag(CommandReceivedHandler.STATUS_TAG, "processed"), MetricTag.getTag(CommandReceivedHandler.TYPE_TAG, "slash"))) - .build(); + .builder() + .name(CommandReceivedHandler.COMMAND_PROCESSED) + .tagList(Arrays.asList(MetricTag.getTag(CommandReceivedHandler.STATUS_TAG, "processed"), MetricTag.getTag(CommandReceivedHandler.TYPE_TAG, "slash"))) + .build(); public List getSlashCommands() { - if(commands == null || commands.isEmpty()) { + if (commands == null || commands.isEmpty()) { return new ArrayList<>(); } return commands.stream() - .filter(command -> command.getConfiguration() - .getSlashCommandConfig().isEnabled()) - .collect(Collectors.toList()); + .filter(command -> command.getConfiguration() + .getSlashCommandConfig().isEnabled()) + .collect(Collectors.toList()); } @Override public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { Observation observation = Observation.createNotStarted("slash-command-received", this.observationRegistry); observation.observe(() -> { - try { - if(commands == null || commands.isEmpty()) return; - if(ContextUtils.hasGuild(event.getInteraction())) { - log.debug("Executing slash command in guild {} from user {}.", event.getGuild().getIdLong(), event.getUser().getIdLong()); - } else { - log.debug("Executing slash command by user {}", event.getUser().getIdLong()); - } - Span span = tracer.currentSpan(); - CompletableFuture.runAsync(() -> { - try (Tracer.SpanInScope ws = tracer.withSpan(span)) { - self.executeListenerLogic(event).whenComplete((unused, throwable) -> { - span.end(); - }); + try { + if (commands == null || commands.isEmpty()) { + return; } - - }, slashCommandExecutor).exceptionally(throwable -> { - log.error("Failed to execute listener logic in async slash command event.", throwable); - return null; - }); - } catch (Exception exception) { - log.error("Failed to process slash command interaction event.", exception); + if (ContextUtils.hasGuild(event.getInteraction())) { + log.debug("Executing slash command in guild {} from user {}.", event.getGuild().getIdLong(), event.getUser().getIdLong()); + } else { + log.debug("Executing slash command by user {}", event.getUser().getIdLong()); + } + Span span = tracer.currentSpan(); + CompletableFuture.runAsync(() -> { + try (Tracer.SpanInScope ws = tracer.withSpan(span)) { + self.executeListenerLogic(event).whenComplete((unused, throwable) -> { + span.end(); + }); + } + + }, slashCommandExecutor).exceptionally(throwable -> { + log.error("Failed to execute listener logic in async slash command event.", throwable); + return null; + }); + } catch (Exception exception) { + log.error("Failed to process slash command interaction event.", exception); + } }); } @@ -165,28 +167,28 @@ public class SlashCommandListenerBean extends ListenerAdapter { @Transactional public CompletableFuture executeListenerLogic(SlashCommandInteractionEvent event) { Optional potentialCommand = findCommand(event); - if(potentialCommand.isPresent()) { + if (potentialCommand.isPresent()) { Command command = potentialCommand.get(); - metricService.incrementCounter(SLASH_COMMANDS_PROCESSED_COUNTER); - try { - Span span = tracer.currentSpan(); - span.tag("command-name", command.getConfiguration().getName()); - return commandService.isCommandExecutable(command, event).thenCompose(conditionResult -> { - try (Tracer.SpanInScope ws = tracer.withSpan(span)) { - return self.executeCommand(event, command, conditionResult); - } - }).exceptionally(throwable -> { - log.error("Error while executing command {}", command.getConfiguration().getName(), throwable); - CommandResult commandResult = CommandResult.fromError(throwable.getMessage(), throwable); - self.executePostCommandListener(command, event, commandResult); - return null; - }); - } catch (Exception exception) { - log.error("Error while checking if command {} is executable.", command.getConfiguration().getName(), exception); - CommandResult commandResult = CommandResult.fromError(exception.getMessage(), exception); + metricService.incrementCounter(SLASH_COMMANDS_PROCESSED_COUNTER); + try { + Span span = tracer.currentSpan(); + span.tag("command-name", command.getConfiguration().getName()); + return commandService.isCommandExecutable(command, event).thenCompose(conditionResult -> { + try (Tracer.SpanInScope ws = tracer.withSpan(span)) { + return self.executeCommand(event, command, conditionResult); + } + }).exceptionally(throwable -> { + log.error("Error while executing command {}", command.getConfiguration().getName(), throwable); + CommandResult commandResult = CommandResult.fromError(throwable.getMessage(), throwable); self.executePostCommandListener(command, event, commandResult); - return CompletableFuture.completedFuture(null); - } + return null; + }); + } catch (Exception exception) { + log.error("Error while checking if command {} is executable.", command.getConfiguration().getName(), exception); + CommandResult commandResult = CommandResult.fromError(exception.getMessage(), exception); + self.executePostCommandListener(command, event, commandResult); + return CompletableFuture.completedFuture(null); + } } else { return CompletableFuture.completedFuture(null); } @@ -195,11 +197,14 @@ public class SlashCommandListenerBean extends ListenerAdapter { @Override public void onCommandAutoCompleteInteraction(@Nonnull CommandAutoCompleteInteractionEvent event) { try { - if(commands == null || commands.isEmpty()) return; - CompletableFuture.runAsync(() -> self.executeAutCompleteListenerLogic(event), MetricUtils.wrapExecutor(slashCommandAutoCompleteExecutor)).exceptionally(throwable -> { - log.error("Failed to execute listener logic in async auto complete interaction event.", throwable); - return null; - }); + if (commands == null || commands.isEmpty()) { + return; + } + CompletableFuture.runAsync(() -> self.executeAutCompleteListenerLogic(event), MetricUtils.wrapExecutor(slashCommandAutoCompleteExecutor)) + .exceptionally(throwable -> { + log.error("Failed to execute listener logic in async auto complete interaction event.", throwable); + return null; + }); } catch (Exception e) { log.error("Failed to process slash command auto complete interaction.", e); } @@ -212,15 +217,17 @@ public class SlashCommandListenerBean extends ListenerAdapter { try { List fullRepliesList = command.performAutoComplete(event); List replies = fullRepliesList.subList(0, Math.min(fullRepliesList.size(), OptionData.MAX_CHOICES)); - event.replyChoiceStrings(replies).queue(unused -> {}, - throwable -> { - if(ContextUtils.hasGuild(event)) { - log.error("Failed to respond to complete of command {} in guild {} by user {}.", - command.getConfiguration().getName(), event.getGuild().getIdLong(), event.getUser().getIdLong()); - } else { - log.error("Failed to resp ond to complete of command {} for user {}.", command.getConfiguration().getName(), event.getUser().getIdLong()); - } - }); + event.replyChoiceStrings(replies).queue(unused -> { + }, + throwable -> { + if (ContextUtils.hasGuild(event)) { + log.error("Failed to respond to complete of command {} in guild {} by user {}.", + command.getConfiguration().getName(), event.getGuild().getIdLong(), event.getUser().getIdLong()); + } else { + log.error("Failed to resp ond to complete of command {} for user {}.", command.getConfiguration().getName(), + event.getUser().getIdLong()); + } + }); } catch (Exception exception) { log.error("Error while executing autocomplete of command {}.", command.getConfiguration().getName(), exception); } @@ -228,8 +235,9 @@ public class SlashCommandListenerBean extends ListenerAdapter { } @Transactional - public void continueSlashCommand(Long interactionId, ButtonInteractionEvent buttonInteractionEvent, SlashCommandConfirmationPayload slashCommandConfirmationPayload) { - if(COMMANDS_WAITING_FOR_CONFIRMATION.containsKey(interactionId)) { + public void continueSlashCommand(Long interactionId, ButtonInteractionEvent buttonInteractionEvent, + SlashCommandConfirmationPayload slashCommandConfirmationPayload) { + if (COMMANDS_WAITING_FOR_CONFIRMATION.containsKey(interactionId)) { DriedSlashCommand driedSlashCommand = COMMANDS_WAITING_FOR_CONFIRMATION.get(interactionId); Command commandInstance = driedSlashCommand.getCommand(); String commandName = commandInstance.getConfiguration().getName(); @@ -250,23 +258,28 @@ public class SlashCommandListenerBean extends ListenerAdapter { return null; }); } else { - log.warn("Interaction was not found in internal map - not continuing interaction from user {} in server {}.", buttonInteractionEvent.getUser().getIdLong(), buttonInteractionEvent.getGuild().getIdLong()); + log.warn("Interaction was not found in internal map - not continuing interaction from user {} in server {}.", + buttonInteractionEvent.getUser().getIdLong(), buttonInteractionEvent.getGuild().getIdLong()); } } @Transactional - public void cleanupSlashCommandConfirmation(SlashCommandConfirmationPayload slashCommandConfirmationPayload, ButtonInteractionEvent buttonInteractionEvent) { - log.debug("Cleaning up component {} and {}.", slashCommandConfirmationPayload.getConfirmationPayloadId(), slashCommandConfirmationPayload.getAbortPayloadId()); - componentPayloadManagementService.deletePayloads(Arrays.asList(slashCommandConfirmationPayload.getAbortPayloadId(), slashCommandConfirmationPayload.getConfirmationPayloadId())); + public void cleanupSlashCommandConfirmation(SlashCommandConfirmationPayload slashCommandConfirmationPayload, + ButtonInteractionEvent buttonInteractionEvent) { + log.debug("Cleaning up component {} and {}.", slashCommandConfirmationPayload.getConfirmationPayloadId(), + slashCommandConfirmationPayload.getAbortPayloadId()); + componentPayloadManagementService.deletePayloads( + Arrays.asList(slashCommandConfirmationPayload.getAbortPayloadId(), slashCommandConfirmationPayload.getConfirmationPayloadId())); } @Transactional public void removeSlashCommandConfirmationInteraction(Long interactionId, String confirmationPayload, String abortPayload) { - if(COMMANDS_WAITING_FOR_CONFIRMATION.containsKey(interactionId)) { + if (COMMANDS_WAITING_FOR_CONFIRMATION.containsKey(interactionId)) { DriedSlashCommand removedSlashCommand = COMMANDS_WAITING_FOR_CONFIRMATION.remove(interactionId); SlashCommandInteractionEvent event = removedSlashCommand.getEvent(); event.getInteraction().getHook().deleteOriginal().queue(); - log.info("Remove interaction for command {} in server {} from user {}.", removedSlashCommand.getCommand().getConfiguration().getName(), event.getGuild().getIdLong(), event.getUser().getIdLong()); + log.info("Remove interaction for command {} in server {} from user {}.", removedSlashCommand.getCommand().getConfiguration().getName(), + event.getGuild().getIdLong(), event.getUser().getIdLong()); } else { log.info("Did not find interaction to clean up."); } @@ -274,9 +287,9 @@ public class SlashCommandListenerBean extends ListenerAdapter { } @Transactional(rollbackFor = AbstractoRunTimeException.class) - public void executeCommand(SlashCommandInteractionEvent event, Command command, ConditionResult conditionResult) { + public CompletableFuture executeCommand(SlashCommandInteractionEvent event, Command command, ConditionResult conditionResult) { String commandName = command.getConfiguration().getName(); - if(command.getConfiguration().isRequiresConfirmation() && conditionResult.isResult()) { + if (command.getConfiguration().isRequiresConfirmation() && conditionResult.isResult()) { DriedSlashCommand slashCommand = DriedSlashCommand .builder() .command(command) @@ -310,19 +323,22 @@ public class SlashCommandListenerBean extends ListenerAdapter { .commandName(commandName) .build(); Long userId = event.getUser().getIdLong(); - interactionService.replyEmbed(COMMAND_CONFIRMATION_MESSAGE_TEMPLATE_KEY, model, event).thenAccept(interactionHook -> { - log.info("Sent confirmation for command {} in server {} for user {}.", commandName, serverId, userId); - }).exceptionally(throwable -> { - log.warn("Failed to send confirmation for command {} in server {} for user {}.", commandName, serverId, userId); - return null; - }); + CompletableFuture replyFuture = + interactionService.replyEmbed(COMMAND_CONFIRMATION_MESSAGE_TEMPLATE_KEY, model, event).thenAccept(interactionHook -> { + log.info("Sent confirmation for command {} in server {} for user {}.", commandName, serverId, userId); + }).exceptionally(throwable -> { + log.warn("Failed to send confirmation for command {} in server {} for user {}.", commandName, serverId, userId); + return null; + }); scheduleConfirmationDeletion(event.getIdLong(), confirmationId, abortId, serverId); + return replyFuture; } else { CompletableFuture commandOutput; - if(conditionResult.isResult()) { + if (conditionResult.isResult()) { commandOutput = command.executeSlash(event).thenApply(commandResult -> { - if(ContextUtils.hasGuild(event)) { - log.info("Command {} in server {} was executed by user {}.", command.getConfiguration().getName(), event.getGuild().getIdLong(), event.getUser().getIdLong()); + if (ContextUtils.hasGuild(event)) { + log.info("Command {} in server {} was executed by user {}.", command.getConfiguration().getName(), event.getGuild().getIdLong(), + event.getUser().getIdLong()); } else { log.info("Command {} was executed by user {}.", command.getConfiguration().getName(), event.getUser().getId()); } @@ -331,7 +347,7 @@ public class SlashCommandListenerBean extends ListenerAdapter { } else { commandOutput = CompletableFuture.completedFuture(CommandResult.fromCondition(conditionResult)); } - commandOutput.thenAccept(commandResult -> { + return commandOutput.thenAccept(commandResult -> { self.executePostCommandListener(command, event, commandResult); }).exceptionally(throwable -> { log.error("Error while handling post execution of command {}", commandName, throwable); @@ -366,26 +382,26 @@ public class SlashCommandListenerBean extends ListenerAdapter { private Optional findCommand(SlashCommandInteractionEvent event) { return commands - .stream() - .filter(command -> command.getConfiguration().getSlashCommandConfig().isEnabled()) - .filter(command -> command.getConfiguration().getSlashCommandConfig().matchesInteraction(event.getInteraction())) - .findAny(); + .stream() + .filter(command -> command.getConfiguration().getSlashCommandConfig().isEnabled()) + .filter(command -> command.getConfiguration().getSlashCommandConfig().matchesInteraction(event.getInteraction())) + .findAny(); } private Optional findCommand(CommandAutoCompleteInteractionEvent event) { return commands - .stream() - .filter(command -> command.getConfiguration().getSlashCommandConfig().isEnabled()) - .filter(command -> command.getConfiguration().getSlashCommandConfig().matchesInteraction(event.getInteraction())) - .findAny(); + .stream() + .filter(command -> command.getConfiguration().getSlashCommandConfig().isEnabled()) + .filter(command -> command.getConfiguration().getSlashCommandConfig().matchesInteraction(event.getInteraction())) + .findAny(); } @PostConstruct public void filterPostProcessors() { metricService.registerCounter(SLASH_COMMANDS_PROCESSED_COUNTER, "Slash Commands processed"); executions = executions - .stream() - .filter(PostCommandExecution::supportsSlash) - .collect(Collectors.toList()); + .stream() + .filter(PostCommandExecution::supportsSlash) + .collect(Collectors.toList()); } } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java index 528cb5623..7ddb4c8a6 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ChannelServiceBean.java @@ -218,114 +218,116 @@ public class ChannelServiceBean implements ChannelService { Span newSpan = tracer.nextSpan().name("send-message"); try (Tracer.SpanInScope ws = this.tracer.withSpan(newSpan.start())) { newSpan.tag("channel.id", textChannel.getIdLong()); - messageToSend.setEphemeral(false); - if(textChannel instanceof GuildMessageChannel guildMessageChannel) { - long maxFileSize = guildMessageChannel.getGuild().getMaxFileSize(); - // in this case, we cannot upload the file, so we need to fail - messageToSend.getAttachedFiles().forEach(attachedFile -> { - if(attachedFile.getFile().length() > maxFileSize) { - throw new UploadFileTooLargeException(attachedFile.getFile().length(), maxFileSize); - } - }); - } - List> futures = new ArrayList<>(); - List allMessageActions = new ArrayList<>(); - if(messageToSend.getUseComponentsV2() && messageToSend.getComponents() != null && !messageToSend.getComponents().isEmpty()) { - metricService.incrementCounter(MESSAGE_SEND_METRIC); - allMessageActions.add(textChannel.sendMessageComponents(messageToSend.getComponents()).useComponentsV2()); - } else { - Iterator embedIterator = messageToSend.getEmbeds().iterator(); - for (int i = 0; i < messageToSend.getMessages().size(); i++) { + messageToSend.setEphemeral(false); + if (textChannel instanceof GuildMessageChannel guildMessageChannel) { + long maxFileSize = guildMessageChannel.getGuild().getMaxFileSize(); + // in this case, we cannot upload the file, so we need to fail + messageToSend.getAttachedFiles().forEach(attachedFile -> { + if (attachedFile.getFile().length() > maxFileSize) { + throw new UploadFileTooLargeException(attachedFile.getFile().length(), maxFileSize); + } + }); + } + List> futures = new ArrayList<>(); + List allMessageActions = new ArrayList<>(); + if (messageToSend.getUseComponentsV2() && messageToSend.getComponents() != null && !messageToSend.getComponents().isEmpty()) { metricService.incrementCounter(MESSAGE_SEND_METRIC); - String text = messageToSend.getMessages().get(i); + allMessageActions.add(textChannel.sendMessageComponents(messageToSend.getComponents()).useComponentsV2()); + } else { + Iterator embedIterator = messageToSend.getEmbeds().iterator(); + for (int i = 0; i < messageToSend.getMessages().size(); i++) { + metricService.incrementCounter(MESSAGE_SEND_METRIC); + String text = messageToSend.getMessages().get(i); + List messageEmbeds = new ArrayList<>(); + while (embedIterator.hasNext()) { + MessageEmbed embedToAdd = embedIterator.next(); + if ((currentEmbedLength(messageEmbeds) + embedToAdd.getLength()) >= MessageEmbed.EMBED_MAX_LENGTH_BOT) { + break; + } + messageEmbeds.add(embedToAdd); + embedIterator.remove(); + } + MessageCreateAction messageAction = textChannel.sendMessage(text); + if (!messageEmbeds.isEmpty()) { + messageAction.setEmbeds(messageEmbeds); + } + allMessageActions.add(messageAction); + } List messageEmbeds = new ArrayList<>(); - while(embedIterator.hasNext()) { + // reset the iterator, because if the if in the above while iterator loop applied, we already took it out from the iterator + // but we didnt add it yet, so it would be lost + embedIterator = messageToSend.getEmbeds().iterator(); + while (embedIterator.hasNext()) { MessageEmbed embedToAdd = embedIterator.next(); - if((currentEmbedLength(messageEmbeds) + embedToAdd.getLength()) >= MessageEmbed.EMBED_MAX_LENGTH_BOT) { - break; + if ((currentEmbedLength(messageEmbeds) + embedToAdd.getLength()) >= MessageEmbed.EMBED_MAX_LENGTH_BOT && !messageEmbeds.isEmpty()) { + allMessageActions.add(textChannel.sendMessageEmbeds(messageEmbeds)); + metricService.incrementCounter(MESSAGE_SEND_METRIC); + messageEmbeds = new ArrayList<>(); } messageEmbeds.add(embedToAdd); - embedIterator.remove(); } - MessageCreateAction messageAction = textChannel.sendMessage(text); - if(!messageEmbeds.isEmpty()) { - messageAction.setEmbeds(messageEmbeds); - } - allMessageActions.add(messageAction); - } - List messageEmbeds = new ArrayList<>(); - // reset the iterator, because if the if in the above while iterator loop applied, we already took it out from the iterator - // but we didnt add it yet, so it would be lost - embedIterator = messageToSend.getEmbeds().iterator(); - while(embedIterator.hasNext()) { - MessageEmbed embedToAdd = embedIterator.next(); - if((currentEmbedLength(messageEmbeds) + embedToAdd.getLength()) >= MessageEmbed.EMBED_MAX_LENGTH_BOT && !messageEmbeds.isEmpty()) { + + if (!messageEmbeds.isEmpty()) { allMessageActions.add(textChannel.sendMessageEmbeds(messageEmbeds)); metricService.incrementCounter(MESSAGE_SEND_METRIC); - messageEmbeds = new ArrayList<>(); } - messageEmbeds.add(embedToAdd); - } - if(!messageEmbeds.isEmpty()) { - allMessageActions.add(textChannel.sendMessageEmbeds(messageEmbeds)); - metricService.incrementCounter(MESSAGE_SEND_METRIC); - } - - List actionRows = messageToSend.getActionRows(); - if(!actionRows.isEmpty()) { - List> groupedActionRows = ListUtils.partition(actionRows, ComponentService.MAX_BUTTONS_PER_ROW); - for (int i = 0; i < allMessageActions.size(); i++) { - allMessageActions.set(i, allMessageActions.get(i).setComponents(groupedActionRows.get(i))); - } - for (int i = allMessageActions.size(); i < groupedActionRows.size(); i++) { - // TODO maybe possible nicer - allMessageActions.add(textChannel.sendMessage(".").setComponents(groupedActionRows.get(i))); - } - AServer server = null; - if(textChannel instanceof GuildChannel) { - GuildChannel channel = (GuildChannel) textChannel; - server = serverManagementService.loadServer(channel.getGuild()); - } - for (ActionRow row : actionRows) { - for (ActionRowChildComponent component : row) { - if (component instanceof ActionComponent) { - String id = ((ActionComponent) component).getId(); - MessageToSend.ComponentConfig payload = messageToSend.getComponentPayloads().get(id); - if (payload != null && payload.getPersistCallback()) { - componentPayloadManagementService.createPayload(id, payload.getPayload(), payload.getPayloadType(), payload.getComponentOrigin(), server, payload.getComponentType()); + List actionRows = messageToSend.getActionRows(); + if (!actionRows.isEmpty()) { + List> groupedActionRows = ListUtils.partition(actionRows, ComponentService.MAX_BUTTONS_PER_ROW); + for (int i = 0; i < allMessageActions.size(); i++) { + allMessageActions.set(i, allMessageActions.get(i).setComponents(groupedActionRows.get(i))); + } + for (int i = allMessageActions.size(); i < groupedActionRows.size(); i++) { + // TODO maybe possible nicer + allMessageActions.add(textChannel.sendMessage(".").setComponents(groupedActionRows.get(i))); + } + AServer server = null; + if (textChannel instanceof GuildChannel) { + GuildChannel channel = (GuildChannel) textChannel; + server = serverManagementService.loadServer(channel.getGuild()); + } + for (ActionRow row : actionRows) { + for (ActionRowChildComponent component : row) { + if (component instanceof ActionComponent) { + String id = ((ActionComponent) component).getId(); + MessageToSend.ComponentConfig payload = messageToSend.getComponentPayloads().get(id); + if (payload != null && payload.getPersistCallback()) { + componentPayloadManagementService.createPayload(id, payload.getPayload(), payload.getPayloadType(), + payload.getComponentOrigin(), server, payload.getComponentType()); + } } } } } - } - if(messageToSend.hasFilesToSend()) { - List attachedFiles = messageToSend - .getAttachedFiles() - .stream() - .map(AttachedFile::convertToFileUpload) - .collect(Collectors.toList()); - if(!allMessageActions.isEmpty()) { - // in case there has not been a message, we need to increment it - allMessageActions.set(0, allMessageActions.get(0).addFiles(attachedFiles)); - } else { - metricService.incrementCounter(MESSAGE_SEND_METRIC); - allMessageActions.add(textChannel.sendFiles(attachedFiles)); + if (messageToSend.hasFilesToSend()) { + List attachedFiles = messageToSend + .getAttachedFiles() + .stream() + .map(AttachedFile::convertToFileUpload) + .collect(Collectors.toList()); + if (!allMessageActions.isEmpty()) { + // in case there has not been a message, we need to increment it + allMessageActions.set(0, allMessageActions.get(0).addFiles(attachedFiles)); + } else { + metricService.incrementCounter(MESSAGE_SEND_METRIC); + allMessageActions.add(textChannel.sendFiles(attachedFiles)); + } } + Set allowedMentions = allowedMentionService.getAllowedMentionsFor(textChannel, messageToSend); + allMessageActions.forEach(messageAction -> { + if (messageToSend.getReferencedMessageId() != null) { + messageAction = messageAction.setMessageReference(messageToSend.getReferencedMessageId()); + if (messageToSend.getMessageConfig() != null && !messageToSend.getMessageConfig().getMentionsReferencedMessage()) { + messageAction = messageAction.mentionRepliedUser(false); + } + } + futures.add(messageAction.setAllowedMentions(allowedMentions).submit()); + }); } + return futures; } - Set allowedMentions = allowedMentionService.getAllowedMentionsFor(textChannel, messageToSend); - allMessageActions.forEach(messageAction -> { - if(messageToSend.getReferencedMessageId() != null) { - messageAction = messageAction.setMessageReference(messageToSend.getReferencedMessageId()); - if(messageToSend.getMessageConfig() != null && !messageToSend.getMessageConfig().getMentionsReferencedMessage()) { - messageAction = messageAction.mentionRepliedUser(false); - } - } - futures.add(messageAction.setAllowedMentions(allowedMentions).submit()); - }); - return futures; } private Integer currentEmbedLength(List messageEmbeds) {