[AB-154] split up private and guild message received handler, split handlers into async and sync handlers

adapting the tests and improving tests to reduce usage of MockUtils
adding some util methods to message bean
extending cache for cached messages
enabling to build cached messages from messages in DM channels (they are not part of the message cache)
splitting multiple listeners to different beans, for better overview (emote updated)
adding convenience service for reactions specifically
split cached reaction and cached reactions, singular only contains one user, while the later contains all users
fixing liquibase configuration for assigned role user
fixing assignable role not having a transaction
moved caching update a bit earlier in various methods
fixing bug that a manual unmute caused duplicate unmute notification
fixing short scheduled unmute not checking the new mute state
limiting parameters for roll
This commit is contained in:
Sheldan
2020-12-20 19:21:24 +01:00
parent 69aa82e26e
commit fb3ed69650
200 changed files with 4253 additions and 1813 deletions

View File

@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.models.database.ACommand;
import dev.sheldan.abstracto.core.command.service.management.CommandInServerManagementService;
import dev.sheldan.abstracto.core.command.service.management.CommandManagementService;
import dev.sheldan.abstracto.core.listener.ServerConfigListener;
import dev.sheldan.abstracto.core.listener.sync.entity.ServerConfigListener;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -162,10 +162,15 @@ public class CommandServiceBean implements CommandService {
}
}
@Override
public UnParsedCommandParameter getUnParsedCommandParameter(String messageContent) {
return new UnParsedCommandParameter(messageContent);
}
@Override
public CompletableFuture<Parameters> getParametersForCommand(String commandName, Message messageContainingContent) {
String contentStripped = messageContainingContent.getContentRaw();
UnParsedCommandParameter unParsedParameter = new UnParsedCommandParameter(contentStripped);
UnParsedCommandParameter unParsedParameter = getUnParsedCommandParameter(contentStripped);
Command command = commandRegistry.findCommandByParameters(commandName, unParsedParameter);
return commandReceivedHandler.getParsedParameters(unParsedParameter, command, messageContainingContent);
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.core.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:listenerConfig.properties")
public class ListenerConfig {
}

View File

@@ -0,0 +1,117 @@
package dev.sheldan.abstracto.core.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class ListenerExecutorConfig {
@Value("${abstracto.listener.default.maxPoolSize}")
private Integer defaultMaxPoolSize;
@Value("${abstracto.listener.default.corePoolSize}")
private Integer defaultCorePoolSize;
@Value("${abstracto.listener.default.keepAliveSeconds}")
private Integer defaultKeepAliveSeconds;
@Autowired
private Environment environment;
private static final String LISTENER_PREFIX = "abstracto.listener.";
private static final String LISTENER_MAX_POOL_SIZE = "maxPoolSize";
private static final String LISTENER_CORE_POOL_SIZE = "corePoolSize";
private static final String LISTENER_KEEP_ALIVE_SECONDS = "keepAliveSeconds";
@Bean(name = "joinListenerExecutor")
public TaskExecutor joinListenerExecutor() {
return setupExecutorFor("joinListener");
}
@Bean(name = "leaveListenerExecutor")
public TaskExecutor leaveListenerExecutor() {
return setupExecutorFor("leaveListener");
}
@Bean(name = "messageReceivedExecutor")
public TaskExecutor messageReceivedExecutor() {
return setupExecutorFor("messageReceivedListener");
}
@Bean(name = "messageDeletedExecutor")
public TaskExecutor messageDeletedExecutor() {
return setupExecutorFor("messageReceivedListener");
}
@Bean(name = "messageEmbeddedExecutor")
public TaskExecutor messageEmbeddedExecutor() {
return setupExecutorFor("messageEmbeddedListener");
}
@Bean(name = "messageUpdatedExecutor")
public TaskExecutor messageUpdatedExecutor() {
return setupExecutorFor("messageUpdatedListener");
}
@Bean(name = "privateMessageReceivedExecutor")
public TaskExecutor privateMessageReceivedExecutor() {
return setupExecutorFor("privateMessageReceivedListener");
}
@Bean(name = "emoteCreatedExecutor")
public TaskExecutor emoteCreatedExecutor() {
return setupExecutorFor("emoteCreatedListener");
}
@Bean(name = "emoteDeletedExecutor")
public TaskExecutor emoteDeletedExecutor() {
return setupExecutorFor("emoteDeletedListener");
}
@Bean(name = "emoteUpdatedExecutor")
public TaskExecutor emoteUpdatedExecutor() {
return setupExecutorFor("emoteUpdatedListener");
}
@Bean(name = "reactionAddedExecutor")
public TaskExecutor reactionAddedExecutor() {
return setupExecutorFor("reactionAddedListener");
}
@Bean(name = "reactionRemovedExecutor")
public TaskExecutor reactionRemovedExecutor() {
return setupExecutorFor("reactionRemovedListener");
}
@Bean(name = "reactionClearedExecutor")
public TaskExecutor reactionClearedExecutor() {
return setupExecutorFor("reactionClearedListener");
}
public ThreadPoolTaskExecutor setupExecutorFor(String listenerName) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
Integer maxPoolSize = getPropertyValueInteger(listenerName, LISTENER_MAX_POOL_SIZE, defaultMaxPoolSize.toString());
Integer corePoolSize = getPropertyValueInteger(listenerName, LISTENER_CORE_POOL_SIZE, defaultCorePoolSize.toString());
Integer keepAliveSeconds = getPropertyValueInteger(listenerName, LISTENER_KEEP_ALIVE_SECONDS, defaultKeepAliveSeconds.toString());
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setKeepAliveSeconds(keepAliveSeconds);
executor.setThreadNamePrefix(listenerName + "-task-executor-thread");
executor.initialize();
return executor;
}
public String getPropertyValue(String listenerName, String key, String defaultValue) {
return environment.getProperty(LISTENER_PREFIX + listenerName + "." + key, defaultValue);
}
public Integer getPropertyValueInteger(String listenerName, String key, String defaultValue) {
return Integer.parseInt(getPropertyValue(listenerName, key, defaultValue));
}
}

View File

@@ -1,114 +0,0 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.events.emote.EmoteAddedEvent;
import net.dv8tion.jda.api.events.emote.EmoteRemovedEvent;
import net.dv8tion.jda.api.events.emote.update.EmoteUpdateNameEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Comparator;
import java.util.List;
@Service
@Slf4j
public class EmoteListener extends ListenerAdapter {
@Autowired
private List<EmoteCreatedListener> createdListeners;
@Autowired
private List<EmoteDeletedListener> deletedListeners;
@Autowired
private List<EmoteUpdatedListener> updatedListeners;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureModeService featureModeService;
@Autowired
@Lazy
private EmoteListener self;
@Override
@Transactional
public void onEmoteAdded(@NotNull EmoteAddedEvent event) {
createdListeners.forEach(listener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(listener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
if(!featureModeService.necessaryFeatureModesMet(listener, event.getGuild().getIdLong())) {
return;
}
self.executeCreatedListener(listener, event.getEmote());
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeCreatedListener(EmoteCreatedListener listener, Emote createDdEmote) {
listener.emoteCreated(createDdEmote);
}
@Override
public void onEmoteRemoved(@NotNull EmoteRemovedEvent event) {
deletedListeners.forEach(listener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(listener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
if(!featureModeService.necessaryFeatureModesMet(listener, event.getGuild().getIdLong())) {
return;
}
self.executeDeletedListener(listener, event.getEmote());
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeDeletedListener(EmoteDeletedListener listener, Emote createDdEmote) {
listener.emoteDeleted(createDdEmote);
}
@Override
public void onEmoteUpdateName(@NotNull EmoteUpdateNameEvent event) {
updatedListeners.forEach(emoteUpdatedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(emoteUpdatedListener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
if(!featureModeService.necessaryFeatureModesMet(emoteUpdatedListener, event.getGuild().getIdLong())) {
return;
}
self.executeUpdatedListener(emoteUpdatedListener, event.getEmote(), event.getOldName(), event.getNewName());
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeUpdatedListener(EmoteUpdatedListener listener, Emote updatedEmote, String oldName, String newName) {
listener.emoteUpdated(updatedEmote, oldName, newName);
}
@PostConstruct
public void postConstruct() {
createdListeners.sort(Comparator.comparing(Prioritized::getPriority).reversed());
deletedListeners.sort(Comparator.comparing(Prioritized::getPriority).reversed());
updatedListeners.sort(Comparator.comparing(Prioritized::getPriority).reversed());
}
}

View File

@@ -1,99 +0,0 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.api.events.message.priv.PrivateMessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
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.Comparator;
import java.util.List;
@Component
@Slf4j
public class MessageReceivedListenerBean extends ListenerAdapter {
@Autowired
private MessageCache messageCache;
@Autowired
private List<MessageReceivedListener> listenerList;
@Autowired
private List<PrivateMessageReceivedListener> privateMessageReceivedListeners;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private ExceptionService exceptionService;
@Autowired
private MessageReceivedListenerBean self;
@Override
public void onGuildMessageReceived(@Nonnull GuildMessageReceivedEvent event) {
messageCache.putMessageInCache(event.getMessage());
listenerList.forEach(messageReceivedListener -> {
try {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageReceivedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
self.executeIndividualGuildMessageReceivedListener(event, messageReceivedListener);
} catch (Exception e) {
log.error("Listener {} had exception when executing.", messageReceivedListener, e);
exceptionService.reportExceptionToGuildMessageReceivedContext(e, event);
}
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualGuildMessageReceivedListener(@Nonnull GuildMessageReceivedEvent event, MessageReceivedListener messageReceivedListener) {
messageReceivedListener.execute(event.getMessage());
}
@Override
public void onPrivateMessageReceived(@Nonnull PrivateMessageReceivedEvent event) {
if(event.getAuthor().getId().equals(botService.getInstance().getSelfUser().getId())) {
return;
}
privateMessageReceivedListeners.forEach(messageReceivedListener -> {
try {
self.executeIndividualPrivateMessageReceivedListener(event, messageReceivedListener);
} catch (Exception e) {
log.error("Listener {} had exception when executing.", messageReceivedListener, e);
exceptionService.reportExceptionToPrivateMessageReceivedContext(e, event);
}
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualPrivateMessageReceivedListener(@Nonnull PrivateMessageReceivedEvent event, PrivateMessageReceivedListener messageReceivedListener) {
log.trace("Executing private message listener {} for member {}.", messageReceivedListener.getClass().getName(), event.getAuthor().getId());
messageReceivedListener.execute(event.getMessage());
}
@PostConstruct
public void postConstruct() {
listenerList.sort(Comparator.comparing(Prioritized::getPriority).reversed());
privateMessageReceivedListeners.sort(Comparator.comparing(Prioritized::getPriority).reversed());
}
}

View File

@@ -1,210 +0,0 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReaction;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveAllEvent;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
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.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ReactionUpdatedListener extends ListenerAdapter {
@Autowired
private MessageCache messageCache;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private List<ReactedAddedListener> addedListenerList;
@Autowired
private List<ReactionClearedListener> clearedListenerList;
@Autowired
private List<ReactedRemovedListener> reactionRemovedListeners;
@Autowired
private ReactionUpdatedListener self;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private EmoteService emoteService;
@Override
@Transactional
public void onGuildMessageReactionAdd(@Nonnull GuildMessageReactionAddEvent event) {
if(event.getUserIdLong() == botService.getInstance().getSelfUser().getIdLong()) {
return;
}
CompletableFuture<CachedMessage> asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());
asyncMessageFromCache.thenAccept(cachedMessage ->
messageCache.getCachedReactionFromReaction(event.getReaction()).thenAccept(reaction -> {
self.callAddedListeners(event, cachedMessage, reaction);
messageCache.putMessageInCache(cachedMessage);
}).exceptionally(throwable -> {
log.error("Failed to handle add reaction to message {} ", event.getMessageIdLong(), throwable);
return null;
})
).exceptionally(throwable -> {
log.error("Message retrieval {} from cache failed. ", event.getMessageIdLong(), throwable);
return null;
});
}
private void addReactionIfNotThere(CachedMessage message, CachedReaction reaction, AUserInAServer userReacting) {
Optional<CachedReaction> existingReaction = message.getReactions().stream().filter(reaction1 ->
emoteService.compareAEmote(reaction1.getEmote(), reaction.getEmote())
).findAny();
if(!existingReaction.isPresent()) {
message.getReactions().add(reaction);
} else {
CachedReaction cachedReaction = existingReaction.get();
Optional<Long> any = cachedReaction.getUserInServersIds().stream().filter(user -> user.equals(userReacting.getUserInServerId())).findAny();
if(!any.isPresent()){
cachedReaction.getUserInServersIds().add(userReacting.getUserInServerId());
}
}
}
private void removeReactionIfThere(CachedMessage message, CachedReaction reaction, AUserInAServer userReacting) {
Optional<CachedReaction> existingReaction = message.getReactions().stream().filter(reaction1 ->
emoteService.compareAEmote(reaction1.getEmote(), reaction.getEmote())
).findAny();
if(existingReaction.isPresent()) {
CachedReaction cachedReaction = existingReaction.get();
cachedReaction.getUserInServersIds().removeIf(user -> user.equals(userReacting.getUserInServerId()));
message.getReactions().removeIf(reaction1 -> reaction1.getUserInServersIds().isEmpty());
}
}
@Transactional
public void callAddedListeners(@Nonnull GuildMessageReactionAddEvent event, CachedMessage cachedMessage, CachedReaction reaction) {
AUserInAServer userInAServer = userInServerManagementService.loadUser(event.getGuild().getIdLong(), event.getUserIdLong());
addReactionIfNotThere(cachedMessage, reaction, userInAServer);
addedListenerList.forEach(reactedAddedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(reactedAddedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
try {
self.executeIndividiualReactionAddedListener(event, cachedMessage, userInAServer, reactedAddedListener);
} catch (Exception e) {
log.warn(String.format("Failed to execute reaction added listener %s.", reactedAddedListener.getClass().getName()), e);
}
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividiualReactionAddedListener(@Nonnull GuildMessageReactionAddEvent event, CachedMessage cachedMessage, AUserInAServer userInAServer, ReactedAddedListener reactedAddedListener) {
reactedAddedListener.executeReactionAdded(cachedMessage, event, userInAServer);
}
@Override
@Transactional
public void onGuildMessageReactionRemove(@Nonnull GuildMessageReactionRemoveEvent event) {
if(event.getUserIdLong() == botService.getInstance().getSelfUser().getIdLong()) {
return;
}
CompletableFuture<CachedMessage> asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());
asyncMessageFromCache.thenAccept(cachedMessage -> {
messageCache.getCachedReactionFromReaction(event.getReaction()).thenAccept(reaction ->
self.callRemoveListeners(event, cachedMessage, reaction)
) .exceptionally(throwable -> {
log.error("Failed to retrieve cached reaction for message {} ", event.getMessageIdLong(), throwable);
return null;
});
messageCache.putMessageInCache(cachedMessage);
}).exceptionally(throwable -> {
log.error("Message retrieval {} from cache failed. ", event.getMessageIdLong(), throwable);
return null;
});
}
@Transactional
public void callRemoveListeners(@Nonnull GuildMessageReactionRemoveEvent event, CachedMessage cachedMessage, CachedReaction reaction) {
AUserInAServer userInAServer = userInServerManagementService.loadUser(event.getGuild().getIdLong(), event.getUserIdLong());
removeReactionIfThere(cachedMessage, reaction, userInAServer);
reactionRemovedListeners.forEach(reactionRemovedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(reactionRemovedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
try {
self.executeIndividualReactionRemovedListener(event, cachedMessage, userInAServer, reactionRemovedListener);
} catch (AbstractoRunTimeException e) {
log.warn(String.format("Failed to execute reaction removed listener %s.", reactionRemovedListener.getClass().getName()), e);
}
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualReactionRemovedListener(@Nonnull GuildMessageReactionRemoveEvent event, CachedMessage cachedMessage, AUserInAServer userInAServer, ReactedRemovedListener reactionRemovedListener) {
reactionRemovedListener.executeReactionRemoved(cachedMessage, event, userInAServer);
}
@Transactional
public void callClearListeners(@Nonnull GuildMessageReactionRemoveAllEvent event, CachedMessage cachedMessage) {
clearedListenerList.forEach(reactionRemovedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(reactionRemovedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
try {
reactionRemovedListener.executeReactionCleared(cachedMessage);
} catch (AbstractoRunTimeException e) {
log.warn(String.format("Failed to execute reaction clear listener %s.", reactionRemovedListener.getClass().getName()), e);
}
});
}
@Override
@Transactional
public void onGuildMessageReactionRemoveAll(@Nonnull GuildMessageReactionRemoveAllEvent event) {
CompletableFuture<CachedMessage> asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());
asyncMessageFromCache.thenAccept(cachedMessage -> {
cachedMessage.getReactions().clear();
messageCache.putMessageInCache(cachedMessage);
self.callClearListeners(event, cachedMessage);
}) .exceptionally(throwable -> {
log.error("Message retrieval from cache failed for message {}", event.getMessageIdLong(), throwable);
return null;
});
}
@PostConstruct
public void postConstruct() {
reactionRemovedListeners.sort(Comparator.comparing(Prioritized::getPriority).reversed());
addedListenerList.sort(Comparator.comparing(Prioritized::getPriority).reversed());
clearedListenerList.sort(Comparator.comparing(Prioritized::getPriority).reversed());
}
}

View File

@@ -0,0 +1,76 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.service.CacheEntityService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.emote.EmoteAddedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Service
@Slf4j
public class AsyncEmoteCreatedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncEmoteCreatedListener> createdListeners;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureModeService featureModeService;
@Autowired
private AsyncEmoteCreatedListenerBean self;
@Autowired
@Qualifier("emoteCreatedExecutor")
private TaskExecutor emoteCreatedExecutor;
@Autowired
private CacheEntityService cacheEntityService;
@Override
@Transactional
public void onEmoteAdded(@NotNull EmoteAddedEvent event) {
if(createdListeners == null) return;
CachedEmote cachedEmote = cacheEntityService.getCachedEmoteFromEmote(event.getEmote(), event.getGuild());
createdListeners.forEach(emoteUpdatedListener ->
CompletableFuture.runAsync(() ->
self.executeCreatedListener(emoteUpdatedListener, cachedEmote, event.getGuild().getIdLong())
, emoteCreatedExecutor)
.exceptionally(throwable -> {
log.error("Async join listener {} failed with exception.", emoteUpdatedListener, throwable);
return null;
})
);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeCreatedListener(AsyncEmoteCreatedListener listener, CachedEmote createDdEmote, Long serverId) {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(listener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, serverId)) {
return;
}
if(!featureModeService.necessaryFeatureModesMet(listener, serverId)) {
return;
}
listener.emoteCreated(createDdEmote);
}
}

View File

@@ -0,0 +1,76 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.service.CacheEntityService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.emote.EmoteRemovedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Service
@Slf4j
public class AsyncEmoteDeletedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncEmoteDeletedListener> deletedListeners;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureModeService featureModeService;
@Autowired
private AsyncEmoteDeletedListenerBean self;
@Autowired
@Qualifier("emoteDeletedExecutor")
private TaskExecutor emoteCreatedExecutor;
@Autowired
private CacheEntityService cacheEntityService;
@Override
public void onEmoteRemoved(@NotNull EmoteRemovedEvent event) {
if(deletedListeners == null) return;
CachedEmote cachedEmote = cacheEntityService.getCachedEmoteFromEmote(event.getEmote(), event.getGuild());
deletedListeners.forEach(emoteUpdatedListener ->
CompletableFuture.runAsync(() ->
self.executeDeletedListener(emoteUpdatedListener, cachedEmote, event.getGuild().getIdLong())
, emoteCreatedExecutor)
.exceptionally(throwable -> {
log.error("Async join listener {} failed with exception.", emoteUpdatedListener, throwable);
return null;
})
);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeDeletedListener(AsyncEmoteDeletedListener listener, CachedEmote deletedEmote, Long serverId) {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(listener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, serverId)) {
return;
}
if(!featureModeService.necessaryFeatureModesMet(listener, serverId)) {
return;
}
listener.emoteDeleted(deletedEmote);
}
}

View File

@@ -0,0 +1,76 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.service.CacheEntityService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.emote.update.EmoteUpdateNameEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Service
@Slf4j
public class AsyncEmoteUpdatedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncEmoteUpdatedListener> updatedListeners;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureModeService featureModeService;
@Autowired
private AsyncEmoteUpdatedListenerBean self;
@Autowired
@Qualifier("emoteUpdatedExecutor")
private TaskExecutor emoteUpdatedExecutor;
@Autowired
private CacheEntityService cacheEntityService;
@Override
public void onEmoteUpdateName(@NotNull EmoteUpdateNameEvent event) {
if(updatedListeners == null) return;
CachedEmote cachedEmote = cacheEntityService.getCachedEmoteFromEmote(event.getEmote(), event.getGuild());
updatedListeners.forEach(emoteUpdatedListener ->
CompletableFuture.runAsync(() ->
self.executeUpdatedListener(emoteUpdatedListener, cachedEmote, event.getOldName(), event.getNewName(), event.getGuild().getIdLong())
, emoteUpdatedExecutor)
.exceptionally(throwable -> {
log.error("Async join listener {} failed with exception.", emoteUpdatedListener, throwable);
return null;
})
);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeUpdatedListener(AsyncEmoteUpdatedListener listener, CachedEmote updatedEmote, String oldName, String newName, Long serverId) {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(listener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, serverId)) {
return;
}
if(!featureModeService.necessaryFeatureModesMet(listener, serverId)) {
return;
}
listener.emoteUpdated(updatedEmote, oldName, newName);
}
}

View File

@@ -0,0 +1,78 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class AsyncJoinListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncJoinListener> listenerList;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private AsyncJoinListenerBean self;
@Autowired
@Qualifier("joinListenerExecutor")
private TaskExecutor joinListenerExecutor;
@Override
@Transactional
public void onGuildMemberJoin(@Nonnull GuildMemberJoinEvent event) {
if(listenerList == null) return;
listenerList.forEach(joinListener -> {
ServerUser serverUser = ServerUser
.builder()
.serverId(event.getGuild().getIdLong())
.userId(event.getUser().getIdLong())
.build();
CompletableFuture.runAsync(() ->
self.executeIndividualJoinListener(joinListener, serverUser)
, joinListenerExecutor).exceptionally(throwable -> {
log.error("Async join listener {} failed with exception.", joinListener, throwable);
return null;
});
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualJoinListener(AsyncJoinListener joinListener, ServerUser serverUser) {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(joinListener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, serverUser.getServerId())) {
return;
}
log.trace("Executing async join listener {} for user {} in server {}.", joinListener, serverUser.getServerId(), serverUser.getUserId());
try {
joinListener.execute(serverUser);
} catch (Exception e) {
log.error("Listener {} failed with exception:", joinListener.getClass().getName(), e);
}
}
}

View File

@@ -0,0 +1,75 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class AsyncLeaveListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncLeaveListener> listenerList;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
@Qualifier("leaveListenerExecutor")
private TaskExecutor leaveListenerExecutor;
@Autowired
private AsyncLeaveListenerBean self;
@Override
@Transactional
public void onGuildMemberRemove(@Nonnull GuildMemberRemoveEvent event) {
if(listenerList == null) return;
listenerList.forEach(leaveListener -> {
ServerUser serverUser = ServerUser
.builder()
.userId(event.getUser().getIdLong())
.serverId(event.getGuild().getIdLong())
.build();
CompletableFuture.runAsync(() ->
self.executeIndividualLeaveListener(serverUser, leaveListener)
, leaveListenerExecutor).exceptionally(throwable -> {
log.error("Async leave listener {} threw exception.", leaveListener, throwable);
return null;
});
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualLeaveListener(ServerUser user, AsyncLeaveListener leaveListener) {
log.trace("Executing leave listener {} for member {} in guild {}.", leaveListener.getClass().getName(), user.getUserId(), user.getServerId());
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(leaveListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, user.getServerId())) {
return;
}
try {
leaveListener.execute(user);
} catch (AbstractoRunTimeException e) {
log.error("Listener {} failed with exception:", leaveListener.getClass().getName(), e);
}
}
}

View File

@@ -0,0 +1,101 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.GuildMessageDeleteEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
@Component
@Slf4j
public class AsyncMessageDeletedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncMessageDeletedListener> listener;
@Autowired
private MessageCache messageCache;
@Autowired
private AsyncMessageDeletedListenerBean self;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private BotService botService;
@Autowired
@Qualifier("messageDeletedExecutor")
private TaskExecutor messageDeletedListenerExecutor;
@Override
@Transactional
public void onGuildMessageDelete(@Nonnull GuildMessageDeleteEvent event) {
if(listener == null) return;
Consumer<CachedMessage> cachedMessageConsumer = cachedMessage -> self.executeListener(cachedMessage);
messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong())
.thenAccept(cachedMessageConsumer)
.exceptionally(throwable -> {
log.error("Message retrieval {} from cache failed. ", event.getMessageIdLong(), throwable);
return null;
});
}
@Transactional
public void executeListener(CachedMessage cachedMessage) {
listener.forEach(messageDeletedListener ->
CompletableFuture.runAsync(() ->
self.executeIndividualMessageDeletedListener(cachedMessage, messageDeletedListener)
, messageDeletedListenerExecutor).exceptionally(throwable -> {
log.error("Async message deleted {} failed with exception.", messageDeletedListener, throwable);
return null;
})
);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualMessageDeletedListener(CachedMessage cachedMessage, AsyncMessageDeletedListener messageDeletedListener) {
log.trace("Executing message deleted listener {} for message {} in guild {}.", messageDeletedListener.getClass().getName(), cachedMessage.getMessageId(), cachedMessage.getMessageId());
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageDeletedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, cachedMessage.getServerId())) {
return;
}
try {
messageDeletedListener.execute(cachedMessage);
} catch (AbstractoRunTimeException e) {
log.error("Listener {} failed with exception:", messageDeletedListener.getClass().getName(), e);
}
}
}

View File

@@ -0,0 +1,91 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.models.listener.GuildMessageEmbedEventModel;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.GuildMessageEmbedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class AsyncMessageEmbeddedListenerBean extends ListenerAdapter {
@Autowired
private MessageCache messageCache;
@Autowired(required = false)
private List<AsyncMessageEmbeddedListener> listenerList;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private ExceptionService exceptionService;
@Autowired
@Qualifier("messageEmbeddedExecutor")
private TaskExecutor messageEmbeddedListener;
@Autowired
private AsyncMessageEmbeddedListenerBean self;
@Override
public void onGuildMessageEmbed(@NotNull GuildMessageEmbedEvent event) {
if(listenerList == null) return;
GuildMessageEmbedEventModel model = buildModel(event);
listenerList.forEach(messageReceivedListener ->
CompletableFuture.runAsync(() ->
self.executeIndividualGuildMessageReceivedListener(model, messageReceivedListener)
, messageEmbeddedListener).exceptionally(throwable -> {
log.error("Async message embedded listener {} failed with exception.", messageReceivedListener, throwable);
return null;
})
);
}
private GuildMessageEmbedEventModel buildModel(GuildMessageEmbedEvent event) {
return GuildMessageEmbedEventModel
.builder()
.channelId(event.getChannel().getIdLong())
.serverId(event.getGuild().getIdLong())
.embeds(event.getMessageEmbeds())
.messageId(event.getMessageIdLong())
.build();
}
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
public void executeIndividualGuildMessageReceivedListener(GuildMessageEmbedEventModel model, AsyncMessageEmbeddedListener messageReceivedListener) {
try {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageReceivedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, model.getServerId())) {
return;
}
messageReceivedListener.execute(model);
} catch (Exception e) {
log.error("Listener {} had exception when executing.", messageReceivedListener, e);
}
}
}

View File

@@ -0,0 +1,78 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class AsyncMessageReceivedListenerBean extends ListenerAdapter {
@Autowired
private MessageCache messageCache;
@Autowired(required = false)
private List<AsyncMessageReceivedListener> listenerList;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private ExceptionService exceptionService;
@Autowired
@Qualifier("messageReceivedExecutor")
private TaskExecutor messageReceivedExecutor;
@Autowired
private AsyncMessageReceivedListenerBean self;
@Override
public void onGuildMessageReceived(@Nonnull GuildMessageReceivedEvent event) {
if(listenerList == null) return;
messageCache.putMessageInCache(event.getMessage()).thenAccept(message -> {
for (AsyncMessageReceivedListener messageReceivedListener : listenerList) {
CompletableFuture.runAsync(() -> self.executeIndividualGuildMessageReceivedListener(message, messageReceivedListener), messageReceivedExecutor)
.exceptionally(throwable -> {
log.error("Async message received listener {} failed.", messageReceivedListener, throwable);
return null;
});
}
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualGuildMessageReceivedListener(CachedMessage cachedMessage, AsyncMessageReceivedListener messageReceivedListener) {
try {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageReceivedListener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, cachedMessage.getServerId())) {
return;
}
messageReceivedListener.execute(cachedMessage);
} catch (Exception e) {
log.error("Async listener {} had exception when executing.", messageReceivedListener, e);
}
}
}

View File

@@ -0,0 +1,83 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.message.guild.GuildMessageUpdateEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class AsyncMessageUpdatedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<AsyncMessageTextUpdatedListener> listener;
@Autowired
private MessageCache messageCache;
@Autowired
private AsyncMessageUpdatedListenerBean self;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
@Qualifier("messageUpdatedExecutor")
private TaskExecutor messageUpdatedExecutor;
@Override
public void onGuildMessageUpdate(GuildMessageUpdateEvent event) {
if(listener == null) return;
Message message = event.getMessage();
messageCache.getMessageFromCache(message.getGuild().getIdLong(), message.getTextChannel().getIdLong(), event.getMessageIdLong())
.thenAcceptBoth(messageCache.putMessageInCache(message), (oldCache, newCache) -> self.executeListener(newCache, oldCache))
.exceptionally(throwable -> {
log.error("Message retrieval {} from cache failed. ", event.getMessage().getId(), throwable);
return null;
});
}
@Transactional
public void executeListener(CachedMessage updatedMessage, CachedMessage oldMessage) {
listener.forEach(messageTextUpdatedListener ->
CompletableFuture
.runAsync(() -> self.executeIndividualMessageUpdatedListener(updatedMessage, oldMessage, messageTextUpdatedListener), messageUpdatedExecutor)
.exceptionally(throwable -> {
log.error("Async message receiver listener {} failed.", messageTextUpdatedListener, throwable);
return null;
})
);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualMessageUpdatedListener(CachedMessage updatedMessage, CachedMessage cachedMessage, AsyncMessageTextUpdatedListener messageTextUpdatedListener) {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageTextUpdatedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, cachedMessage.getServerId())) {
return;
}
try {
messageTextUpdatedListener.execute(cachedMessage, updatedMessage);
} catch (AbstractoRunTimeException e) {
log.error(String.format("Failed to execute listener. %s", messageTextUpdatedListener.getClass().getName()), e);
}
}
}

View File

@@ -0,0 +1,74 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.CacheEntityService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.priv.PrivateMessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class AsyncPrivateMessageReceivedListenerBean extends ListenerAdapter {
@Autowired
private BotService botService;
@Autowired
private ExceptionService exceptionService;
@Autowired(required = false)
private List<AsyncPrivateMessageReceivedListener> privateMessageReceivedListeners;
@Autowired
private AsyncPrivateMessageReceivedListenerBean self;
@Autowired
private CacheEntityService cacheEntityService;
@Autowired
@Qualifier("privateMessageReceivedExecutor")
private TaskExecutor privateMessageReceivedExecutor;
@Override
public void onPrivateMessageReceived(@Nonnull PrivateMessageReceivedEvent event) {
if(privateMessageReceivedListeners == null) return;
if(event.getAuthor().getId().equals(botService.getInstance().getSelfUser().getId())) {
return;
}
cacheEntityService.buildCachedMessageFromMessage(event.getMessage()).thenAccept(cachedMessage ->
privateMessageReceivedListeners.forEach(messageReceivedListener -> {
try {
CompletableFuture.runAsync(() ->
self.executeIndividualPrivateMessageReceivedListener(cachedMessage, messageReceivedListener)
, privateMessageReceivedExecutor)
.exceptionally(throwable -> {
log.error("Async private message receiver listener {} failed.", messageReceivedListener, throwable);
return null;
});
} catch (Exception e) {
log.error("Private message received {} had exception when executing.", messageReceivedListener, e);
exceptionService.reportExceptionToPrivateMessageReceivedContext(e, event);
}
})
);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualPrivateMessageReceivedListener(CachedMessage cachedMessage, AsyncPrivateMessageReceivedListener messageReceivedListener) {
log.trace("Executing private message listener {} for member {}.", messageReceivedListener.getClass().getName(), cachedMessage.getAuthor().getAuthorId());
messageReceivedListener.execute(cachedMessage);
}
}

View File

@@ -0,0 +1,123 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class AsyncReactionAddedListenerBean extends ListenerAdapter {
@Autowired
private CacheEntityService cacheEntityService;
@Autowired
private MessageCache messageCache;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired(required = false)
private List<AsyncReactionAddedListener> addedListenerList;
@Autowired
private AsyncReactionAddedListenerBean self;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private EmoteService emoteService;
@Autowired
@Qualifier("reactionAddedExecutor")
private TaskExecutor reactionAddedTaskExecutor;
@Override
@Transactional
public void onGuildMessageReactionAdd(@Nonnull GuildMessageReactionAddEvent event) {
if(addedListenerList == null) return;
if(event.getUserIdLong() == botService.getInstance().getSelfUser().getIdLong()) {
return;
}
CompletableFuture<CachedMessage> asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());
asyncMessageFromCache.thenAccept(cachedMessage ->
cacheEntityService.getCachedReactionFromReaction(event.getReaction()).thenAccept(reaction ->
self.callAddedListeners(event, cachedMessage, reaction)
).exceptionally(throwable -> {
log.error("Failed to handle add reaction to message {} ", event.getMessageIdLong(), throwable);
return null;
})
).exceptionally(throwable -> {
log.error("Message retrieval {} from cache failed. ", event.getMessageIdLong(), throwable);
return null;
});
}
private void addReactionIfNotThere(CachedMessage message, CachedReactions reaction, ServerUser userReacting) {
Optional<CachedReactions> existingReaction = message.getReactions().stream().filter(reaction1 ->
reaction1.getEmote().equals(reaction.getEmote())
).findAny();
if(!existingReaction.isPresent()) {
message.getReactions().add(reaction);
} else {
CachedReactions cachedReaction = existingReaction.get();
Optional<ServerUser> any = cachedReaction.getUsers().stream().filter(user -> user.getUserId().equals(userReacting.getUserId()) && user.getServerId().equals(userReacting.getServerId())).findAny();
if(!any.isPresent()){
cachedReaction.getUsers().add(userReacting);
}
}
}
@Transactional
public void callAddedListeners(@Nonnull GuildMessageReactionAddEvent event, CachedMessage cachedMessage, CachedReactions reaction) {
ServerUser serverUser = ServerUser.builder().serverId(event.getGuild().getIdLong()).userId(event.getUserIdLong()).build();
addReactionIfNotThere(cachedMessage, reaction, serverUser);
messageCache.putMessageInCache(cachedMessage);
addedListenerList.forEach(reactedAddedListener ->
CompletableFuture.runAsync(() ->
self.executeIndividualReactionAddedListener(reaction, cachedMessage, serverUser, reactedAddedListener)
, reactionAddedTaskExecutor)
.exceptionally(throwable -> {
log.error("Async reaction added listener {} failed with exception.", reactedAddedListener, throwable);
return null;
})
);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualReactionAddedListener(@Nonnull CachedReactions reaction, CachedMessage cachedMessage, ServerUser serverUser, AsyncReactionAddedListener reactedAddedListener) {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(reactedAddedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, serverUser.getServerId())) {
return;
}
try {
reactedAddedListener.executeReactionAdded(cachedMessage, reaction, serverUser);
} catch (Exception e) {
log.warn(String.format("Failed to execute reaction added listener %s.", reactedAddedListener.getClass().getName()), e);
}
}
}

View File

@@ -0,0 +1,98 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveAllEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class AsyncReactionClearedListenerBean extends ListenerAdapter {
@Autowired
private CacheEntityService cacheEntityService;
@Autowired
private MessageCache messageCache;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired(required = false)
private List<AsyncReactionClearedListener> clearedListenerList;
@Autowired
private AsyncReactionClearedListenerBean self;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private EmoteService emoteService;
@Autowired
@Qualifier("reactionClearedExecutor")
private TaskExecutor reactionClearedExecutor;
@Transactional
public void callClearListeners(CachedMessage cachedMessage) {
if(clearedListenerList == null) return;
clearedListenerList.forEach(reactionRemovedListener ->
CompletableFuture.runAsync(() ->
self.callConcreteListener(cachedMessage, reactionRemovedListener)
, reactionClearedExecutor)
.exceptionally(throwable -> {
log.error("Async reaction cleared listener {} failed with exception.", reactionRemovedListener, throwable);
return null;
})
);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void callConcreteListener(CachedMessage cachedMessage, AsyncReactionClearedListener reactionRemovedListener) {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(reactionRemovedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, cachedMessage.getServerId())) {
return;
}
try {
reactionRemovedListener.executeReactionCleared(cachedMessage);
} catch (AbstractoRunTimeException e) {
log.warn(String.format("Failed to execute reaction clear listener %s.", reactionRemovedListener.getClass().getName()), e);
}
}
@Override
@Transactional
public void onGuildMessageReactionRemoveAll(@Nonnull GuildMessageReactionRemoveAllEvent event) {
CompletableFuture<CachedMessage> asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());
asyncMessageFromCache.thenAccept(cachedMessage -> {
cachedMessage.getReactions().clear();
messageCache.putMessageInCache(cachedMessage);
self.callClearListeners(cachedMessage);
}) .exceptionally(throwable -> {
log.error("Message retrieval from cache failed for message {}", event.getMessageIdLong(), throwable);
return null;
});
}
}

View File

@@ -0,0 +1,121 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class AsyncReactionRemovedListenerBean extends ListenerAdapter {
@Autowired
private CacheEntityService cacheEntityService;
@Autowired
private MessageCache messageCache;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired(required = false)
private List<AsyncReactionRemovedListener> reactionRemovedListeners;
@Autowired
private AsyncReactionRemovedListenerBean self;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private EmoteService emoteService;
@Autowired
@Qualifier("reactionRemovedExecutor")
private TaskExecutor reactionRemovedExecutor;
private void removeReactionIfThere(CachedMessage message, CachedReactions reaction, ServerUser serverUser) {
Optional<CachedReactions> existingReaction = message.getReactions().stream().filter(reaction1 ->
reaction1.getEmote().equals(reaction.getEmote())
).findAny();
if(existingReaction.isPresent()) {
CachedReactions cachedReaction = existingReaction.get();
cachedReaction.getUsers().removeIf(user -> user.getUserId().equals(serverUser.getUserId()) && user.getServerId().equals(serverUser.getServerId()));
message.getReactions().removeIf(reaction1 -> reaction1.getUsers().isEmpty());
}
}
@Override
@Transactional
public void onGuildMessageReactionRemove(@Nonnull GuildMessageReactionRemoveEvent event) {
if(reactionRemovedListeners == null) return;
if(event.getUserIdLong() == botService.getInstance().getSelfUser().getIdLong()) {
return;
}
CompletableFuture<CachedMessage> asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());
asyncMessageFromCache.thenAccept(cachedMessage -> {
messageCache.putMessageInCache(cachedMessage);
cacheEntityService.getCachedReactionFromReaction(event.getReaction()).thenAccept(reaction ->
self.callRemoveListeners(event, cachedMessage, reaction)
) .exceptionally(throwable -> {
log.error("Failed to retrieve cached reaction for message {} ", event.getMessageIdLong(), throwable);
return null;
});
}).exceptionally(throwable -> {
log.error("Message retrieval {} from cache failed. ", event.getMessageIdLong(), throwable);
return null;
});
}
@Transactional
public void callRemoveListeners(@Nonnull GuildMessageReactionRemoveEvent event, CachedMessage cachedMessage, CachedReactions reaction) {
ServerUser serverUser = ServerUser.builder().serverId(cachedMessage.getServerId()).userId(event.getUserIdLong()).build();
removeReactionIfThere(cachedMessage, reaction, serverUser);
reactionRemovedListeners.forEach(reactionRemovedListener -> {
try {
CompletableFuture.runAsync(() ->
self.executeIndividualReactionRemovedListener(reaction, cachedMessage, serverUser, reactionRemovedListener)
, reactionRemovedExecutor)
.exceptionally(throwable -> {
log.error("Async reaction removed listener {} failed with exception.", reactionRemovedListener, throwable);
return null;
});
} catch (AbstractoRunTimeException e) {
log.warn(String.format("Failed to execute reaction removed listener %s.", reactionRemovedListener.getClass().getName()), e);
}
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualReactionRemovedListener(@Nonnull CachedReactions reaction, CachedMessage cachedMessage, ServerUser userRemoving, AsyncReactionRemovedListener reactionRemovedListener) {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(reactionRemovedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, cachedMessage.getServerId())) {
return;
}
reactionRemovedListener.executeReactionRemoved(cachedMessage, reaction, userRemoving);
}
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener.entity;
package dev.sheldan.abstracto.core.listener.sync.entity;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import org.springframework.beans.factory.annotation.Autowired;
@@ -8,7 +8,7 @@ import java.util.List;
@Component
public class ChannelGroupCreatedListenerManager {
@Autowired
@Autowired(required = false)
private List<ChannelGroupCreatedListener> listener;
@Autowired

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener.entity;
package dev.sheldan.abstracto.core.listener.sync.entity;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import org.springframework.beans.factory.annotation.Autowired;
@@ -8,7 +8,7 @@ import java.util.List;
@Component
public class ChannelGroupDeletedListenerManager {
@Autowired
@Autowired(required = false)
private List<ChannelGroupDeletedListener> listener;
public void executeListener(AChannelGroup createdGroup){

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.entity;
import dev.sheldan.abstracto.core.command.service.CommandManager;
import dev.sheldan.abstracto.core.models.database.AServer;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.entity;
import dev.sheldan.abstracto.core.models.database.AFeature;
import dev.sheldan.abstracto.core.models.database.AServer;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.models.database.AChannelType;
@@ -25,6 +25,8 @@ public class ChannelListener extends ListenerAdapter {
@Autowired
private ChannelManagementService channelManagementService;
// TODO move this to a separate sync listener, which implements a a generic channel listener, in order to provide
// the channel listener for other implementations
@Override
@Transactional
public void onTextChannelDelete(@Nonnull TextChannelDeleteEvent event) {

View File

@@ -0,0 +1,67 @@
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.events.emote.EmoteAddedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.List;
@Service
@Slf4j
public class EmoteCreatedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<EmoteCreatedListener> createdListeners;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureModeService featureModeService;
@Autowired
@Lazy
private EmoteCreatedListenerBean self;
@Override
@Transactional
public void onEmoteAdded(@NotNull EmoteAddedEvent event) {
if(createdListeners == null) return;
createdListeners.forEach(listener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(listener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
if(!featureModeService.necessaryFeatureModesMet(listener, event.getGuild().getIdLong())) {
return;
}
self.executeCreatedListener(listener, event.getEmote());
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeCreatedListener(EmoteCreatedListener listener, Emote createDdEmote) {
listener.emoteCreated(createDdEmote);
}
@PostConstruct
public void postConstruct() {
BeanUtils.sortPrioritizedListeners(createdListeners);
}
}

View File

@@ -0,0 +1,66 @@
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.events.emote.EmoteRemovedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.List;
@Service
@Slf4j
public class EmoteDeletedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<EmoteDeletedListener> deletedListeners;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureModeService featureModeService;
@Autowired
@Lazy
private EmoteDeletedListenerBean self;
@Override
public void onEmoteRemoved(@NotNull EmoteRemovedEvent event) {
if(deletedListeners == null) return;
deletedListeners.forEach(listener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(listener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
if(!featureModeService.necessaryFeatureModesMet(listener, event.getGuild().getIdLong())) {
return;
}
self.executeDeletedListener(listener, event.getEmote());
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeDeletedListener(EmoteDeletedListener listener, Emote createDdEmote) {
listener.emoteDeleted(createDdEmote);
}
@PostConstruct
public void postConstruct() {
BeanUtils.sortPrioritizedListeners(deletedListeners);
}
}

View File

@@ -0,0 +1,66 @@
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.events.emote.update.EmoteUpdateNameEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.List;
@Service
@Slf4j
public class EmoteUpdatedListenerBean extends ListenerAdapter {
@Autowired(required = false)
private List<EmoteUpdatedListener> updatedListeners;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureModeService featureModeService;
@Autowired
@Lazy
private EmoteUpdatedListenerBean self;
@Override
public void onEmoteUpdateName(@NotNull EmoteUpdateNameEvent event) {
if(updatedListeners == null) return;
updatedListeners.forEach(emoteUpdatedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(emoteUpdatedListener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
if(!featureModeService.necessaryFeatureModesMet(emoteUpdatedListener, event.getGuild().getIdLong())) {
return;
}
self.executeUpdatedListener(emoteUpdatedListener, event.getEmote(), event.getOldName(), event.getNewName());
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeUpdatedListener(EmoteUpdatedListener listener, Emote updatedEmote, String oldName, String newName) {
listener.emoteUpdated(updatedEmote, oldName, newName);
}
@PostConstruct
public void postConstruct() {
BeanUtils.sortPrioritizedListeners(updatedListeners);
}
}

View File

@@ -1,10 +1,11 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
@@ -15,15 +16,13 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@Service
@Slf4j
public class JoinListenerBean extends ListenerAdapter {
@Autowired
@Autowired(required = false)
private List<JoinListener> listenerList;
@Autowired
@@ -41,6 +40,7 @@ public class JoinListenerBean extends ListenerAdapter {
@Override
@Transactional
public void onGuildMemberJoin(@Nonnull GuildMemberJoinEvent event) {
if(listenerList == null) return;
listenerList.forEach(joinListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(joinListener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
@@ -63,6 +63,6 @@ public class JoinListenerBean extends ListenerAdapter {
@PostConstruct
public void postConstruct() {
listenerList.sort(Comparator.comparing(Prioritized::getPriority).reversed());
BeanUtils.sortPrioritizedListeners(listenerList);
}
}

View File

@@ -1,9 +1,10 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
@@ -14,14 +15,13 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
import java.util.Comparator;
import java.util.List;
@Service
@Slf4j
public class LeaveListenerBean extends ListenerAdapter {
@Autowired
@Autowired(required = false)
private List<LeaveListener> listenerList;
@Autowired
@@ -36,6 +36,7 @@ public class LeaveListenerBean extends ListenerAdapter {
@Override
@Transactional
public void onGuildMemberRemove(@Nonnull GuildMemberRemoveEvent event) {
if(listenerList == null) return;
listenerList.forEach(leaveListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(leaveListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
@@ -57,6 +58,6 @@ public class LeaveListenerBean extends ListenerAdapter {
@PostConstruct
public void postConstruct() {
listenerList.sort(Comparator.comparing(Prioritized::getPriority).reversed());
BeanUtils.sortPrioritizedListeners(listenerList);
}
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
@@ -13,6 +13,7 @@ import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.GuildMessageDeleteEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
@@ -23,14 +24,13 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
@Component
@Slf4j
public class MessageDeletedListenerBean extends ListenerAdapter {
@Autowired
@Autowired(required = false)
private List<MessageDeletedListener> listener;
@Autowired
@@ -60,6 +60,7 @@ public class MessageDeletedListenerBean extends ListenerAdapter {
@Override
@Transactional
public void onGuildMessageDelete(@Nonnull GuildMessageDeleteEvent event) {
if(listener == null) return;
Consumer<CachedMessage> cachedMessageConsumer = cachedMessage -> self.executeListener(cachedMessage);
messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong())
.thenAccept(cachedMessageConsumer)
@@ -76,14 +77,14 @@ public class MessageDeletedListenerBean extends ListenerAdapter {
.builder()
.guild(serverManagementService.loadOrCreate(cachedMessage.getServerId()))
.channel(channelManagementService.loadChannel(cachedMessage.getChannelId()))
.aUserInAServer(userInServerManagementService.loadUser(cachedMessage.getServerId(), cachedMessage.getAuthorId()))
.aUserInAServer(userInServerManagementService.loadUser(cachedMessage.getServerId(), cachedMessage.getAuthor().getAuthorId()))
.build();
botService.getMemberInServerAsync(cachedMessage.getServerId(), cachedMessage.getAuthorId()).thenAccept(member -> {
botService.getMemberInServerAsync(cachedMessage.getServerId(), cachedMessage.getAuthor().getAuthorId()).thenAccept(member -> {
GuildChannelMember authorMember = GuildChannelMember
.builder()
.guild(botService.getGuildById(cachedMessage.getServerId()))
.textChannel(botService.getTextChannelFromServerOptional(cachedMessage.getServerId(), cachedMessage.getChannelId()).orElseThrow(() -> new ChannelNotInGuildException(cachedMessage.getChannelId())))
.member(botService.getMemberInServer(cachedMessage.getServerId(), cachedMessage.getAuthorId()))
.member(botService.getMemberInServer(cachedMessage.getServerId(), cachedMessage.getAuthor().getAuthorId()))
.build();
listener.forEach(messageDeletedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageDeletedListener.getFeature());
@@ -111,6 +112,6 @@ public class MessageDeletedListenerBean extends ListenerAdapter {
@PostConstruct
public void postConstruct() {
listener.sort(Comparator.comparing(Prioritized::getPriority).reversed());
BeanUtils.sortPrioritizedListeners(listener);
}
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.config.FeatureConfig;
@@ -7,6 +7,7 @@ import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.GuildMessageEmbedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
@@ -18,7 +19,6 @@ import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Comparator;
import java.util.List;
@Component
@@ -28,7 +28,7 @@ public class MessageEmbeddedListenerBean extends ListenerAdapter {
@Autowired
private MessageCache messageCache;
@Autowired
@Autowired(required = false)
private List<MessageEmbeddedListener> listenerList;
@Autowired
@@ -48,6 +48,7 @@ public class MessageEmbeddedListenerBean extends ListenerAdapter {
@Override
public void onGuildMessageEmbed(@NotNull GuildMessageEmbedEvent event) {
if(listenerList == null) return;
GuildMessageEmbedEventModel model = buildModel(event);
listenerList.forEach(messageReceivedListener -> {
try {
@@ -58,7 +59,6 @@ public class MessageEmbeddedListenerBean extends ListenerAdapter {
self.executeIndividualGuildMessageReceivedListener(model, messageReceivedListener);
} catch (Exception e) {
log.error("Listener {} had exception when executing.", messageReceivedListener, e);
// exceptionService.reportExceptionToGuildMessageReceivedContext(e, event);
}
});
}
@@ -66,7 +66,8 @@ public class MessageEmbeddedListenerBean extends ListenerAdapter {
private GuildMessageEmbedEventModel buildModel(GuildMessageEmbedEvent event) {
return GuildMessageEmbedEventModel
.builder()
.channel(event.getChannel())
.channelId(event.getChannel().getIdLong())
.serverId(event.getGuild().getIdLong())
.embeds(event.getMessageEmbeds())
.messageId(event.getMessageIdLong())
.build();
@@ -79,6 +80,6 @@ public class MessageEmbeddedListenerBean extends ListenerAdapter {
@PostConstruct
public void postConstruct() {
listenerList.sort(Comparator.comparing(Prioritized::getPriority).reversed());
BeanUtils.sortPrioritizedListeners(listenerList);
}
}

View File

@@ -0,0 +1,78 @@
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.execution.result.MessageReceivedListenerResult;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
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.List;
@Component
@Slf4j
public class MessageReceivedListenerBean extends ListenerAdapter {
@Autowired
private MessageCache messageCache;
@Autowired(required = false)
private List<MessageReceivedListener> listenerList;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private ExceptionService exceptionService;
@Autowired
private MessageReceivedListenerBean self;
@Override
public void onGuildMessageReceived(@Nonnull GuildMessageReceivedEvent event) {
messageCache.putMessageInCache(event.getMessage());
if(listenerList == null) return;
for (MessageReceivedListener messageReceivedListener : listenerList) {
try {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageReceivedListener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
continue;
}
MessageReceivedListenerResult result = self.executeIndividualGuildMessageReceivedListener(event, messageReceivedListener);
if (messageReceivedListener.shouldConsume(event, result)) {
break;
}
} catch (Exception e) {
log.error("Listener {} had exception when executing.", messageReceivedListener, e);
exceptionService.reportExceptionToGuildMessageReceivedContext(e, event);
}
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public MessageReceivedListenerResult executeIndividualGuildMessageReceivedListener(@Nonnull GuildMessageReceivedEvent event, MessageReceivedListener messageReceivedListener) {
return messageReceivedListener.execute(event.getMessage());
}
@PostConstruct
public void postConstruct() {
BeanUtils.sortPrioritizedListeners(listenerList);
}
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
@@ -6,6 +6,7 @@ import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.message.guild.GuildMessageUpdateEvent;
@@ -17,21 +18,20 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
import java.util.Comparator;
import java.util.List;
@Component
@Slf4j
public class MessageUpdatedListener extends ListenerAdapter {
public class MessageUpdatedListenerBean extends ListenerAdapter {
@Autowired
@Autowired(required = false)
private List<MessageTextUpdatedListener> listener;
@Autowired
private MessageCache messageCache;
@Autowired
private MessageUpdatedListener self;
private MessageUpdatedListenerBean self;
@Autowired
private FeatureFlagService featureFlagService;
@@ -41,6 +41,7 @@ public class MessageUpdatedListener extends ListenerAdapter {
@Override
public void onGuildMessageUpdate(@Nonnull GuildMessageUpdateEvent event) {
if(listener == null) return;
Message message = event.getMessage();
messageCache.getMessageFromCache(message.getGuild().getIdLong(), message.getTextChannel().getIdLong(), event.getMessageIdLong()).thenAccept(cachedMessage -> {
self.executeListener(message, cachedMessage);
@@ -74,6 +75,6 @@ public class MessageUpdatedListener extends ListenerAdapter {
@PostConstruct
public void postConstruct() {
listener.sort(Comparator.comparing(Prioritized::getPriority).reversed());
BeanUtils.sortPrioritizedListeners(listener);
}
}

View File

@@ -0,0 +1,62 @@
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.priv.PrivateMessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
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.List;
@Component
@Slf4j
public class PrivateMessageReceivedListenerBean extends ListenerAdapter {
@Autowired
private BotService botService;
@Autowired
private ExceptionService exceptionService;
@Autowired(required = false)
private List<PrivateMessageReceivedListener> privateMessageReceivedListeners;
@Autowired
private PrivateMessageReceivedListenerBean self;
@Override
public void onPrivateMessageReceived(@Nonnull PrivateMessageReceivedEvent event) {
if(privateMessageReceivedListeners == null) return;
if(event.getAuthor().getId().equals(botService.getInstance().getSelfUser().getId())) {
return;
}
privateMessageReceivedListeners.forEach(messageReceivedListener -> {
try {
self.executeIndividualPrivateMessageReceivedListener(event, messageReceivedListener);
} catch (Exception e) {
log.error("Listener {} had exception when executing.", messageReceivedListener, e);
exceptionService.reportExceptionToPrivateMessageReceivedContext(e, event);
}
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualPrivateMessageReceivedListener(@Nonnull PrivateMessageReceivedEvent event, PrivateMessageReceivedListener messageReceivedListener) {
// no feature flag check, because we are in no server context
log.trace("Executing private message listener {} for member {}.", messageReceivedListener.getClass().getName(), event.getAuthor().getId());
messageReceivedListener.execute(event.getMessage());
}
@PostConstruct
public void postConstruct() {
BeanUtils.sortPrioritizedListeners(privateMessageReceivedListeners);
}
}

View File

@@ -0,0 +1,119 @@
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
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.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ReactionAddedListenerBean extends ListenerAdapter {
@Autowired
private CacheEntityService cacheEntityService;
@Autowired
private MessageCache messageCache;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired(required = false)
private List<ReactionAddedListener> addedListenerList;
@Autowired
private ReactionAddedListenerBean self;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private EmoteService emoteService;
@Override
@Transactional
public void onGuildMessageReactionAdd(@Nonnull GuildMessageReactionAddEvent event) {
if(addedListenerList == null) return;
if(event.getUserIdLong() == botService.getInstance().getSelfUser().getIdLong()) {
return;
}
CompletableFuture<CachedMessage> asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());
asyncMessageFromCache.thenAccept(cachedMessage ->
cacheEntityService.getCachedReactionFromReaction(event.getReaction()).thenAccept(reaction -> {
self.callAddedListeners(event, cachedMessage, reaction);
messageCache.putMessageInCache(cachedMessage);
}).exceptionally(throwable -> {
log.error("Failed to handle add reaction to message {} ", event.getMessageIdLong(), throwable);
return null;
})
).exceptionally(throwable -> {
log.error("Message retrieval {} from cache failed. ", event.getMessageIdLong(), throwable);
return null;
});
}
private void addReactionIfNotThere(CachedMessage message, CachedReactions reaction, ServerUser userReacting) {
Optional<CachedReactions> existingReaction = message.getReactions().stream().filter(reaction1 ->
reaction1.getEmote().equals(reaction.getEmote())
).findAny();
if(!existingReaction.isPresent()) {
message.getReactions().add(reaction);
} else {
CachedReactions cachedReaction = existingReaction.get();
Optional<ServerUser> any = cachedReaction.getUsers().stream().filter(user -> user.getServerId().equals(userReacting.getServerId()) && user.getUserId().equals(userReacting.getUserId())).findAny();
if(!any.isPresent()){
cachedReaction.getUsers().add(userReacting);
}
}
}
@Transactional
public void callAddedListeners(@Nonnull GuildMessageReactionAddEvent event, CachedMessage cachedMessage, CachedReactions reaction) {
ServerUser serverUser = ServerUser.builder().serverId(cachedMessage.getServerId()).userId(event.getUserIdLong()).build();
addReactionIfNotThere(cachedMessage, reaction, serverUser);
addedListenerList.forEach(reactedAddedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(reactedAddedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
try {
self.executeIndividualReactionAddedListener(event, cachedMessage, serverUser, reactedAddedListener);
} catch (Exception e) {
log.warn(String.format("Failed to execute reaction added listener %s.", reactedAddedListener.getClass().getName()), e);
}
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualReactionAddedListener(@Nonnull GuildMessageReactionAddEvent event, CachedMessage cachedMessage, ServerUser serverUser, ReactionAddedListener reactedAddedListener) {
reactedAddedListener.executeReactionAdded(cachedMessage, event, serverUser);
}
@PostConstruct
public void postConstruct() {
BeanUtils.sortPrioritizedListeners(addedListenerList);
}
}

View File

@@ -0,0 +1,88 @@
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveAllEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
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.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ReactionClearedListenerBean extends ListenerAdapter {
@Autowired
private CacheEntityService cacheEntityService;
@Autowired
private MessageCache messageCache;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired(required = false)
private List<ReactionClearedListener> clearedListenerList;
@Autowired
private ReactionClearedListenerBean self;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private EmoteService emoteService;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void callClearListeners(@Nonnull GuildMessageReactionRemoveAllEvent event, CachedMessage cachedMessage) {
if(clearedListenerList == null) return;
clearedListenerList.forEach(reactionRemovedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(reactionRemovedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
try {
reactionRemovedListener.executeReactionCleared(cachedMessage);
} catch (AbstractoRunTimeException e) {
log.warn(String.format("Failed to execute reaction clear listener %s.", reactionRemovedListener.getClass().getName()), e);
}
});
}
@Override
@Transactional
public void onGuildMessageReactionRemoveAll(@Nonnull GuildMessageReactionRemoveAllEvent event) {
CompletableFuture<CachedMessage> asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());
asyncMessageFromCache.thenAccept(cachedMessage -> {
cachedMessage.getReactions().clear();
messageCache.putMessageInCache(cachedMessage);
self.callClearListeners(event, cachedMessage);
}) .exceptionally(throwable -> {
log.error("Message retrieval from cache failed for message {}", event.getMessageIdLong(), throwable);
return null;
});
}
@PostConstruct
public void postConstruct() {
BeanUtils.sortPrioritizedListeners(clearedListenerList);
}
}

View File

@@ -0,0 +1,116 @@
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
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.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ReactionRemovedListenerBean extends ListenerAdapter {
@Autowired
private CacheEntityService cacheEntityService;
@Autowired
private MessageCache messageCache;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired(required = false)
private List<ReactionRemovedListener> reactionRemovedListeners;
@Autowired
private ReactionRemovedListenerBean self;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private EmoteService emoteService;
private void removeReactionIfThere(CachedMessage message, CachedReactions reaction, ServerUser userReacting) {
Optional<CachedReactions> existingReaction = message.getReactions().stream().filter(reaction1 ->
reaction1.getEmote().equals(reaction.getEmote())
).findAny();
if(existingReaction.isPresent()) {
CachedReactions cachedReaction = existingReaction.get();
cachedReaction.getUsers().removeIf(user -> user.getUserId().equals(userReacting.getUserId()) && user.getServerId().equals(userReacting.getServerId()));
message.getReactions().removeIf(reaction1 -> reaction1.getUsers().isEmpty());
}
}
@Override
@Transactional
public void onGuildMessageReactionRemove(@Nonnull GuildMessageReactionRemoveEvent event) {
if(reactionRemovedListeners == null) return;
if(event.getUserIdLong() == botService.getInstance().getSelfUser().getIdLong()) {
return;
}
CompletableFuture<CachedMessage> asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong());
asyncMessageFromCache.thenAccept(cachedMessage -> {
cacheEntityService.getCachedReactionFromReaction(event.getReaction()).thenAccept(reaction ->
self.callRemoveListeners(event, cachedMessage, reaction)
) .exceptionally(throwable -> {
log.error("Failed to retrieve cached reaction for message {} ", event.getMessageIdLong(), throwable);
return null;
});
messageCache.putMessageInCache(cachedMessage);
}).exceptionally(throwable -> {
log.error("Message retrieval {} from cache failed. ", event.getMessageIdLong(), throwable);
return null;
});
}
@Transactional
public void callRemoveListeners(@Nonnull GuildMessageReactionRemoveEvent event, CachedMessage cachedMessage, CachedReactions reaction) {
ServerUser serverUser = ServerUser.builder().serverId(event.getGuild().getIdLong()).userId(event.getUserIdLong()).build();
removeReactionIfThere(cachedMessage, reaction, serverUser);
reactionRemovedListeners.forEach(reactionRemovedListener -> {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(reactionRemovedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
try {
self.executeIndividualReactionRemovedListener(event, cachedMessage, serverUser, reactionRemovedListener);
} catch (AbstractoRunTimeException e) {
log.warn(String.format("Failed to execute reaction removed listener %s.", reactionRemovedListener.getClass().getName()), e);
}
});
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void executeIndividualReactionRemovedListener(@Nonnull GuildMessageReactionRemoveEvent event, CachedMessage cachedMessage, ServerUser serverUser, ReactionRemovedListener reactionRemovedListener) {
reactionRemovedListener.executeReactionRemoved(cachedMessage, event, serverUser);
}
@PostConstruct
public void postConstruct() {
BeanUtils.sortPrioritizedListeners(reactionRemovedListeners);
}
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import dev.sheldan.abstracto.core.service.BotService;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.RoleService;

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.listener.sync.entity.ServerConfigListener;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import lombok.extern.slf4j.Slf4j;
@@ -17,7 +18,7 @@ import java.util.List;
@Slf4j
public class ServerJoinListener extends ListenerAdapter {
@Autowired
@Autowired(required = false)
private List<ServerConfigListener> configListeners;
@Autowired
@@ -29,6 +30,7 @@ public class ServerJoinListener extends ListenerAdapter {
@Override
@Transactional
public void onGuildJoin(@Nonnull GuildJoinEvent event) {
if(configListeners == null) return;
log.info("Joining guild {}, executing server config listener.", event.getGuild().getId());
AServer server = serverManagementService.loadOrCreate(event.getGuild().getIdLong());
configListeners.forEach(serverConfigListener -> self.executingIndividualServerConfigListener(server, serverConfigListener));

View File

@@ -17,6 +17,9 @@ public interface UserInServerRepository extends JpaRepository<AUserInAServer, Lo
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
Optional<AUserInAServer> findByServerReferenceAndUserReference(AServer serverId, AUser userId);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
Optional<AUserInAServer> findByServerReference_IdAndUserReference_Id(Long serverId, Long userId);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
boolean existsByServerReferenceAndUserReference(AServer server, AUser user);

View File

@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
@@ -10,10 +11,7 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.requests.GatewayIntent;
import net.dv8tion.jda.api.utils.MemberCachePolicy;
import org.springframework.stereotype.Service;
@@ -95,6 +93,16 @@ public class BotServiceBean implements BotService {
}
}
@Override
public CompletableFuture<Member> retrieveMemberInServer(ServerUser serverUser) {
return getMemberInServerAsync(serverUser.getServerId(), serverUser.getUserId());
}
@Override
public CompletableFuture<User> retrieveUserById(Long userId) {
return instance.retrieveUserById(userId).submit();
}
@Override
public boolean isUserInGuild(AUserInAServer aUserInAServer) {
Guild guildById = instance.getGuildById(aUserInAServer.getServerReference().getId());
@@ -219,9 +227,19 @@ public class BotServiceBean implements BotService {
return guildById;
}
@Override
public CompletableFuture<Guild> retrieveGuildById(Long serverId) {
return null;
}
@Override
public Member getBotInGuild(AServer server) {
Guild guild = getGuildById(server.getId());
return guild.getSelfMember();
}
@Override
public CompletableFuture<User> getUserViaId(Long userId) {
return instance.retrieveUserById(userId).submit();
}
}

View File

@@ -0,0 +1,232 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.requests.restaction.pagination.ReactionPaginationAction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.awt.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
@Slf4j
public class CacheEntityServiceBean implements CacheEntityService {
@Autowired
private EmoteService emoteService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
@Lazy
private CacheEntityServiceBean concreteSelf;
@Override
public CachedEmote getCachedEmoteFromEmote(Emote emote, Guild guild) {
return CachedEmote.builder()
.emoteId(emote.getIdLong())
.emoteName(emote.getName())
.imageURL(emote.getImageUrl())
.external(!emoteService.emoteIsFromGuild(emote, guild))
.custom(true)
.animated(emote.isAnimated())
.build();
}
@Override
public CachedEmote getCachedEmoteFromEmote(MessageReaction.ReactionEmote emote, Guild guild) {
if(emote.isEmoji()) {
return CachedEmote.builder()
.emoteName(emote.getName())
.custom(false)
.build();
} else {
return CachedEmote.builder()
.emoteId(emote.getIdLong())
.emoteName(emote.getName())
.imageURL(emote.getEmote().getImageUrl())
.external(emoteService.emoteIsFromGuild(emote.getEmote(), guild))
.custom(true)
.animated(emote.getEmote().isAnimated())
.build();
}
}
@Override
public CachedAttachment getCachedAttachment(Message.Attachment attachment) {
return CachedAttachment
.builder()
.fileName(attachment.getFileName())
.height(attachment.getHeight())
.proxyUrl(attachment.getProxyUrl())
.size(attachment.getSize())
.url(attachment.getUrl())
.width(attachment.getWidth())
.build();
}
@Override
public CachedEmbed getCachedEmbedFromEmbed(MessageEmbed embed) {
CachedEmbed.CachedEmbedBuilder builder = CachedEmbed
.builder();
MessageEmbed.AuthorInfo author = embed.getAuthor();
if(author != null) {
builder.author(CachedEmbedAuthor.builder().avatar(author.getProxyIconUrl()).url(author.getUrl()).name(author.getName()).build());
}
List<MessageEmbed.Field> fields = embed.getFields();
if(!fields.isEmpty()) {
List<CachedEmbedField> cachedEmbedFields = new ArrayList<>();
fields.forEach(field -> {
CachedEmbedField build = CachedEmbedField
.builder()
.name(field.getName())
.value(field.getValue())
.inline(field.isInline())
.build();
cachedEmbedFields.add(build);
});
builder.fields(cachedEmbedFields);
}
MessageEmbed.ImageInfo image = embed.getImage();
if(image != null) {
builder.cachedImageInfo(buildCachedImage(image));
}
MessageEmbed.Thumbnail thumbnail = embed.getThumbnail();
if(thumbnail != null) {
builder.cachedThumbnail(buildCachedThumbnail(thumbnail));
}
Color color = embed.getColor();
if(color != null) {
CachedEmbedColor build = CachedEmbedColor
.builder()
.r(color.getRed())
.g(color.getGreen())
.b(color.getBlue())
.build();
builder.color(build);
}
builder.description(embed.getDescription());
MessageEmbed.Footer footer = embed.getFooter();
if(footer != null) {
CachedEmbedFooter build = CachedEmbedFooter
.builder()
.icon(footer.getProxyIconUrl())
.text(footer.getText())
.build();
builder.footer(build);
}
return builder.build();
}
@Override
public CachedThumbnail buildCachedThumbnail(MessageEmbed.Thumbnail thumbnail) {
return CachedThumbnail
.builder()
.height(thumbnail.getHeight())
.proxyUrl(thumbnail.getProxyUrl())
.width(thumbnail.getWidth())
.build();
}
@Override
public CachedImageInfo buildCachedImage(MessageEmbed.ImageInfo image) {
return CachedImageInfo
.builder()
.height(image.getHeight())
.proxyUrl(image.getProxyUrl())
.url(image.getUrl())
.width(image.getWidth())
.build();
}
@Override
public CompletableFuture<CachedReactions> getCachedReactionFromReaction(MessageReaction reaction) {
CompletableFuture<CachedReactions> future = new CompletableFuture<>();
ReactionPaginationAction users = reaction.retrieveUsers();
CachedReactions.CachedReactionsBuilder builder = CachedReactions.builder();
List<ServerUser> aUsers = new ArrayList<>();
users.forEachAsync(user -> {
concreteSelf.loadUser(reaction, aUsers, user);
return false;
}).thenAccept(o -> future.complete(builder.build()))
.exceptionally(throwable -> {
log.error("Failed to load reaction users.", throwable);
return null;
});
builder.users(aUsers);
builder.self(reaction.isSelf());
builder.emote(getCachedEmoteFromEmote(reaction.getReactionEmote(), reaction.getGuild()));
return future;
}
@Transactional
public void loadUser(MessageReaction reaction, List<ServerUser> aUsers, User user) {
if(reaction.getGuild() != null) {
aUsers.add(ServerUser.builder().userId(user.getIdLong()).serverId(reaction.getGuild().getIdLong()).build());
}
}
@Override
public CompletableFuture<CachedMessage> buildCachedMessageFromMessage(Message message) {
CompletableFuture<CachedMessage> future = new CompletableFuture<>();
List<CachedAttachment> attachments = new ArrayList<>();
message.getAttachments().forEach(attachment ->
attachments.add(getCachedAttachment(attachment))
);
List<CachedEmbed> embeds = new ArrayList<>();
message.getEmbeds().forEach(embed ->
embeds.add(getCachedEmbedFromEmbed(embed))
);
List<CachedEmote> emotes = new ArrayList<>();
if(message.isFromGuild()) {
message.getEmotesBag().forEach(emote -> emotes.add(getCachedEmoteFromEmote(emote, message.getGuild())));
}
List<CompletableFuture<CachedReactions>> futures = new ArrayList<>();
message.getReactions().forEach(messageReaction -> futures.add(getCachedReactionFromReaction(messageReaction)));
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(aVoid ->
{
CachedAuthor cachedAuthor = CachedAuthor.builder().authorId(message.getAuthor().getIdLong()).isBot(message.getAuthor().isBot()).build();
CachedMessage.CachedMessageBuilder builder = CachedMessage.builder()
.author(cachedAuthor)
.messageId(message.getIdLong())
.channelId(message.getChannel().getIdLong())
.content(message.getContentRaw())
.embeds(embeds)
.emotes(emotes)
.reactions(convertReactionFuturesToCachedReactions(futures))
.timeCreated(Instant.from(message.getTimeCreated()))
.attachments(attachments);
if(message.isFromGuild()) {
builder.serverId(message.getGuild().getIdLong());
}
future.complete(builder
.build());
}
).exceptionally(throwable -> {
log.error("Failed to load reactions for message {}. ", message.getId(), throwable);
return null;
});
return future;
}
private List<CachedReactions> convertReactionFuturesToCachedReactions(List<CompletableFuture<CachedReactions>> futures) {
return futures.stream().map(CompletableFuture::join).collect(Collectors.toList());
}
}

View File

@@ -294,4 +294,15 @@ public class ChannelServiceBean implements ChannelService {
.server(server)
.build();
}
@Override
public CompletableFuture<Message> sendSimpleTemplateToChannel(Long serverId, Long channelId, String template) {
TextChannel textChannel = getTextChannel(serverId, channelId);
return sendTextTemplateInChannel(template, new Object(), textChannel);
}
@Override
public TextChannel getTextChannel(Long serverId, Long channelId) {
return botService.getTextChannelFromServer(serverId, channelId);
}
}

View File

@@ -1,8 +1,9 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.EmoteNotDefinedException;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReaction;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.DefaultEmoteManagementService;
@@ -16,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
@@ -112,8 +114,8 @@ public class EmoteServiceBean implements EmoteService {
}
@Override
public Optional<CachedReaction> getReactionFromMessageByEmote(CachedMessage message, AEmote emote) {
return message.getReactions().stream().filter(reaction -> compareAEmote(reaction.getEmote(), emote)).findFirst();
public Optional<CachedReactions> getReactionFromMessageByEmote(CachedMessage message, AEmote emote) {
return message.getReactions().stream().filter(reaction -> compareCachedEmoteWithAEmote(reaction.getEmote(), emote)).findFirst();
}
@Override
@@ -129,6 +131,19 @@ public class EmoteServiceBean implements EmoteService {
}
}
@Override
public boolean compareCachedEmoteWithAEmote(CachedEmote a, AEmote b) {
if(Boolean.TRUE.equals(a.getCustom()) && Boolean.TRUE.equals(b.getCustom())) {
return a.getEmoteId().equals(b.getEmoteId());
} else {
if(Boolean.FALSE.equals(a.getCustom()) && Boolean.FALSE.equals(b.getCustom())) {
return a.getEmoteName().equals(b.getEmoteKey());
} else {
return false;
}
}
}
@Override
public AEmote getFakeEmote(Object object) {
if(object instanceof Emote) {
@@ -163,4 +178,14 @@ public class EmoteServiceBean implements EmoteService {
return guild.getEmoteById(emote.getId()) != null;
}
@Override
public CompletableFuture<Emote> getEmoteFromCachedEmote(CachedEmote cachedEmote) {
if(!cachedEmote.getCustom()) {
throw new IllegalArgumentException("Given Emote was not a custom emote.");
}
return botService.retrieveGuildById(cachedEmote.getServerId()).thenCompose(guild ->
guild.retrieveEmoteById(cachedEmote.getEmoteId()).submit().thenApply(listedEmote -> listedEmote)
);
}
}

View File

@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.exception.FeatureModeNotFoundException;
import dev.sheldan.abstracto.core.listener.FeatureAware;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.models.template.commands.FeatureModeDisplay;
import dev.sheldan.abstracto.core.service.management.DefaultFeatureModeManagement;

View File

@@ -3,27 +3,17 @@ package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReaction;
import dev.sheldan.abstracto.core.models.cache.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.requests.restaction.pagination.ReactionPaginationAction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.awt.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@Component
@Slf4j
@@ -37,7 +27,7 @@ public class MessageCacheBean implements MessageCache {
private EmoteService emoteService;
@Autowired
private UserInServerManagementService userInServerManagementService;
private CacheEntityService cacheEntityService;
@Autowired
@Lazy
@@ -48,7 +38,7 @@ public class MessageCacheBean implements MessageCache {
@CachePut(key = "#message.id")
public CompletableFuture<CachedMessage> putMessageInCache(Message message) {
log.trace("Adding message {} to cache", message.getId());
return concreteSelf.buildCachedMessageFromMessage(message);
return cacheEntityService.buildCachedMessageFromMessage(message);
}
@@ -83,7 +73,7 @@ public class MessageCacheBean implements MessageCache {
TextChannel textChannel = textChannelByIdOptional.get();
textChannel.retrieveMessageById(messageId).queue(message ->
buildCachedMessageFromMessage(message)
cacheEntityService.buildCachedMessageFromMessage(message)
.thenAccept(future::complete)
.exceptionally(throwable -> {
log.error("Failed to load message for caching.", throwable);
@@ -104,128 +94,5 @@ public class MessageCacheBean implements MessageCache {
return future;
}
@Override
public CompletableFuture<CachedMessage> buildCachedMessageFromMessage(Message message) {
CompletableFuture<CachedMessage> future = new CompletableFuture<>();
List<String> attachmentUrls = new ArrayList<>();
message.getAttachments().forEach(attachment ->
attachmentUrls.add(attachment.getProxyUrl())
);
List<CachedEmbed> embeds = new ArrayList<>();
message.getEmbeds().forEach(embed ->
embeds.add(getCachedEmbedFromEmbed(embed))
);
List<CompletableFuture<CachedReaction>> futures = new ArrayList<>();
message.getReactions().forEach(messageReaction -> futures.add(concreteSelf.getCachedReactionFromReaction(messageReaction)));
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(aVoid ->
future.complete(CachedMessage.builder()
.authorId(message.getAuthor().getIdLong())
.serverId(message.getGuild().getIdLong())
.messageId(message.getIdLong())
.channelId(message.getChannel().getIdLong())
.content(message.getContentRaw())
.embeds(embeds)
.reactions(getFutures(futures))
.timeCreated(Instant.from(message.getTimeCreated()))
.attachmentUrls(attachmentUrls)
.build())
).exceptionally(throwable -> {
log.error("Failed to load reactions for message {}. ", message.getId(), throwable);
return null;
});
return future;
}
private List<CachedReaction> getFutures(List<CompletableFuture<CachedReaction>> futures) {
List<CachedReaction> reactions = new ArrayList<>();
futures.forEach(future -> {
try {
CachedReaction cachedReaction = future.get();
reactions.add(cachedReaction);
} catch (InterruptedException | ExecutionException e) {
log.error("Error while executing future to retrieve reaction.", e);
Thread.currentThread().interrupt();
}
});
return reactions;
}
@Override
public CompletableFuture<CachedReaction> getCachedReactionFromReaction(MessageReaction reaction) {
CompletableFuture<CachedReaction> future = new CompletableFuture<>();
ReactionPaginationAction users = reaction.retrieveUsers().cache(false);
CachedReaction.CachedReactionBuilder builder = CachedReaction.builder();
List<Long> aUsers = new ArrayList<>();
users.forEachAsync(user -> {
concreteSelf.loadUser(reaction, aUsers, user);
return false;
}).thenAccept(o -> future.complete(builder.build()))
.exceptionally(throwable -> {
log.error("Failed to load reaction users.", throwable);
return null;
});
builder.userInServersIds(aUsers);
builder.emote(emoteService.buildAEmoteFromReaction(reaction.getReactionEmote()));
return future;
}
@Transactional
public void loadUser(MessageReaction reaction, List<Long> aUsers, User user) {
if(reaction.getGuild() != null) {
aUsers.add(userInServerManagementService.loadUser(reaction.getGuild().getIdLong(), user.getIdLong()).getUserInServerId());
}
}
private CachedEmbed getCachedEmbedFromEmbed(MessageEmbed embed) {
CachedEmbed.CachedEmbedBuilder builder = CachedEmbed
.builder();
MessageEmbed.AuthorInfo author = embed.getAuthor();
if(author != null) {
builder.author(CachedEmbedAuthor.builder().avatar(author.getProxyIconUrl()).url(author.getUrl()).name(author.getName()).build());
}
List<MessageEmbed.Field> fields = embed.getFields();
if(!fields.isEmpty()) {
List<CachedEmbedField> cachedEmbedFields = new ArrayList<>();
fields.forEach(field -> {
CachedEmbedField build = CachedEmbedField
.builder()
.name(field.getName())
.value(field.getValue())
.inline(field.isInline())
.build();
cachedEmbedFields.add(build);
});
builder.fields(cachedEmbedFields);
}
MessageEmbed.ImageInfo image = embed.getImage();
if(image != null) {
builder.imageUrl(image.getProxyUrl());
}
Color color = embed.getColor();
if(color != null) {
CachedEmbedColor build = CachedEmbedColor
.builder()
.r(color.getRed())
.g(color.getGreen())
.b(color.getBlue())
.build();
builder.color(build);
}
builder.description(embed.getDescription());
MessageEmbed.Footer footer = embed.getFooter();
if(footer != null) {
CachedEmbedFooter build = CachedEmbedFooter
.builder()
.icon(footer.getProxyIconUrl())
.text(footer.getText())
.build();
builder.footer(build);
}
return builder.build();
}
}

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.ConfiguredEmoteNotUsableException;
import dev.sheldan.abstracto.core.exception.EmoteNotInServerException;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
@@ -11,6 +12,7 @@ import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -56,6 +58,13 @@ public class MessageServiceBean implements MessageService {
return message.addReaction(unicode).submit();
}
@Override
public CompletableFuture<Void> addDefaultReactionToMessageAsync(String unicode, Long serverId, Long channelId, Long messageId) {
TextChannel channel = botService.getTextChannelFromServer(serverId, channelId);
return channel.retrieveMessageById(messageId).submit()
.thenCompose(message -> self.addDefaultReactionToMessageAsync(unicode, message));
}
@Override
public CompletableFuture<Void> addReactionToMessageWithFuture(String emoteKey, Long serverId, Message message) {
Guild guild = botService.getGuildById(serverId);
@@ -103,6 +112,13 @@ public class MessageServiceBean implements MessageService {
return message.addReaction(emoteById).submit();
}
@Override
public CompletableFuture<Void> addReactionToMessageWithFuture(String emoteKey, Long serverId, Long channelId, Long messageId) {
TextChannel channel = botService.getTextChannelFromServer(serverId, channelId);
return channel.retrieveMessageById(messageId).submit()
.thenCompose(message -> self.addReactionToMessageWithFuture(emoteKey, serverId, message));
}
@Override
public CompletableFuture<Void> removeReactionFromMessageWithFuture(AEmote emote, Message message) {
if(Boolean.TRUE.equals(emote.getCustom())) {
@@ -266,6 +282,14 @@ public class MessageServiceBean implements MessageService {
);
}
@Override
public CompletableFuture<Message> sendSimpleTemplateToUser(Long userId, String templateKey) {
String text = templateService.renderSimpleTemplate(templateKey);
return botService.getUserViaId(userId)
.thenCompose(this::openPrivateChannelForUser)
.thenCompose(o -> channelService.sendTextToChannel(text, o));
}
@Override
public CompletableFuture<Message> sendTemplateToUser(User user, String template, Object model) {
String message = templateService.renderTemplate(template, model);
@@ -274,20 +298,25 @@ public class MessageServiceBean implements MessageService {
@Override
public CompletableFuture<Void> sendEmbedToUser(User user, String template, Object model) {
return user.openPrivateChannel().submit().thenCompose(privateChannel ->
return openPrivateChannelForUser(user).thenCompose(privateChannel ->
FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(template, model, privateChannel)));
}
@NotNull
public CompletableFuture<PrivateChannel> openPrivateChannelForUser(User user) {
return user.openPrivateChannel().submit();
}
@Override
public CompletableFuture<Message> sendEmbedToUserWithMessage(User user, String template, Object model) {
log.trace("Sending direct message with template {} to user {}.", template, user.getIdLong());
return user.openPrivateChannel().submit().thenCompose(privateChannel ->
return openPrivateChannelForUser(user).thenCompose(privateChannel ->
channelService.sendEmbedTemplateInChannel(template, model, privateChannel).get(0));
}
@Override
public CompletableFuture<Message> sendMessageToSendToUser(User user, MessageToSend messageToSend) {
return user.openPrivateChannel().submit().thenCompose(privateChannel -> channelService.sendMessageToSendToChannel(messageToSend, privateChannel).get(0));
return openPrivateChannelForUser(user).thenCompose(privateChannel -> channelService.sendMessageToSendToChannel(messageToSend, privateChannel).get(0));
}
@Override
@@ -304,6 +333,16 @@ public class MessageServiceBean implements MessageService {
@Override
public CompletableFuture<Void> editMessageInDMChannel(User user, MessageToSend messageToSend, Long messageId) {
return user.openPrivateChannel().submit().thenCompose(privateChannel -> channelService.editMessageInAChannelFuture(messageToSend, privateChannel, messageId).thenApply(message -> null));
return openPrivateChannelForUser(user).thenCompose(privateChannel -> channelService.editMessageInAChannelFuture(messageToSend, privateChannel, messageId).thenApply(message -> null));
}
@Override
public CompletableFuture<Message> loadMessageFromCachedMessage(CachedMessage cachedMessage) {
return loadMessage(cachedMessage.getServerId(), cachedMessage.getChannelId(), cachedMessage.getMessageId());
}
@Override
public CompletableFuture<Message> loadMessage(Long serverId, Long channelId, Long messageId) {
return channelService.getTextChannel(serverId, channelId).retrieveMessageById(messageId).submit();
}
}

View File

@@ -0,0 +1,40 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReaction;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
@Component
public class ReactionServiceBean implements ReactionService {
@Autowired
private MessageService messageService;
@Autowired
private EmoteService emoteService;
@Autowired
private BotService botService;
@Override
public CompletableFuture<Void> removeReactionFromMessage(CachedReaction reaction, CachedMessage cachedMessage) {
return messageService.loadMessageFromCachedMessage(cachedMessage).thenCompose(message -> removeReactionFromMessage(reaction, message));
}
@Override
public CompletableFuture<Void> removeReactionFromMessage(CachedReaction reaction, Message message) {
return botService.retrieveUserById(reaction.getUser().getUserId()).thenCompose(user -> {
if(reaction.getEmote().getCustom()) {
return emoteService.getEmoteFromCachedEmote(reaction.getEmote()).thenCompose(emote ->
message.removeReaction(emote, user).submit()
);
} else {
return message.removeReaction(reaction.getEmote().getEmoteName(), user).submit();
}
});
}
}

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.listener.ServerConfigListener;
import dev.sheldan.abstracto.core.listener.sync.entity.ServerConfigListener;
import dev.sheldan.abstracto.core.utils.SnowflakeUtils;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;

View File

@@ -4,8 +4,8 @@ import dev.sheldan.abstracto.core.command.exception.ChannelAlreadyInChannelGroup
import dev.sheldan.abstracto.core.command.exception.ChannelGroupExistsException;
import dev.sheldan.abstracto.core.command.exception.ChannelGroupNotFoundException;
import dev.sheldan.abstracto.core.command.exception.ChannelNotInChannelGroupException;
import dev.sheldan.abstracto.core.listener.entity.ChannelGroupCreatedListenerManager;
import dev.sheldan.abstracto.core.listener.entity.ChannelGroupDeletedListenerManager;
import dev.sheldan.abstracto.core.listener.sync.entity.ChannelGroupCreatedListenerManager;
import dev.sheldan.abstracto.core.listener.sync.entity.ChannelGroupDeletedListenerManager;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.database.AServer;

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.exception.UserInServerNotFoundException;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
@@ -29,9 +30,17 @@ public class UserInServerManagementServiceBean implements UserInServerManagement
@Override
public AUserInAServer loadUser(Long serverId, Long userId) {
AUser user = userManagementService.loadUser(userId);
AServer server = serverManagementService.loadOrCreate(serverId);
return loadUser(server, user);
return userInServerRepository.findByServerReference_IdAndUserReference_Id(serverId, userId).orElseThrow(() -> new UserInServerNotFoundException(0L));
}
@Override
public AUserInAServer loadUser(ServerUser serverUser) {
return loadUser(serverUser.getServerId(), serverUser.getUserId());
}
@Override
public Optional<AUserInAServer> loadUserOptional(Long serverId, Long userId) {
return userInServerRepository.findByServerReference_IdAndUserReference_Id(serverId, userId);
}
@Override
@@ -49,13 +58,13 @@ public class UserInServerManagementServiceBean implements UserInServerManagement
}
@Override
public Optional<AUserInAServer> loadUserConditional(Long userInServerId) {
public Optional<AUserInAServer> loadUserOptional(Long userInServerId) {
return userInServerRepository.findById(userInServerId);
}
@Override
public AUserInAServer loadUser(Long userInServerId) {
return loadUserConditional(userInServerId).orElseThrow(() -> new UserInServerNotFoundException(userInServerId));
return loadUserOptional(userInServerId).orElseThrow(() -> new UserInServerNotFoundException(userInServerId));
}
@Override

View File

@@ -0,0 +1,17 @@
package dev.sheldan.abstracto.core.utils;
import dev.sheldan.abstracto.core.Prioritized;
import lombok.NoArgsConstructor;
import java.util.Comparator;
import java.util.List;
@NoArgsConstructor
public class BeanUtils {
public static <T extends Prioritized> void sortPrioritizedListeners(List<T> prioritized) {
if(prioritized != null) {
prioritized.sort(Comparator.comparing(Prioritized::getPriority).reversed());
}
}
}

View File

@@ -0,0 +1,8 @@
abstracto.listener.default.maxPoolSize=5
abstracto.listener.default.corePoolSize=1
abstracto.listener.default.keepAliveSeconds=60
abstracto.listener.joinListener.maxPoolSize=5
abstracto.listener.joinListener.corePoolSize=1
abstracto.listener.joinListener.keepAliveSeconds=60

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.listener.sync.entity.FeatureFlagListener;
import dev.sheldan.abstracto.core.models.database.AFeature;
import dev.sheldan.abstracto.core.models.database.AFeatureFlag;
import dev.sheldan.abstracto.core.models.database.AServer;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.FeatureMode;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core;
public interface Prioritized {
Integer getPriority();

View File

@@ -3,7 +3,7 @@ package dev.sheldan.abstracto.core.command;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.listener.FeatureAware;
import dev.sheldan.abstracto.core.FeatureAware;
import java.util.concurrent.CompletableFuture;

View File

@@ -4,6 +4,7 @@ import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.condition.ConditionResult;
import dev.sheldan.abstracto.core.command.config.Parameters;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
import dev.sheldan.abstracto.core.command.models.database.ACommand;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.ARole;
@@ -25,5 +26,6 @@ public interface CommandService {
void disAllowCommandForRole(ACommand aCommand, ARole role);
void disAllowFeatureForRole(FeatureEnum featureEnum, ARole role);
ConditionResult isCommandExecutable(Command command, CommandContext commandContext);
UnParsedCommandParameter getUnParsedCommandParameter(String messageContent);
CompletableFuture<Parameters> getParametersForCommand(String commandName, Message messageContainingContent);
}

View File

@@ -0,0 +1,4 @@
package dev.sheldan.abstracto.core.execution;
public interface ResultAware {
}

View File

@@ -0,0 +1,4 @@
package dev.sheldan.abstracto.core.execution.result;
public interface ExecutionResult {
}

View File

@@ -0,0 +1,5 @@
package dev.sheldan.abstracto.core.execution.result;
public enum MessageReceivedListenerResult implements ExecutionResult {
IGNORED, PROCESSED, DELETED
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.execution.result.ExecutionResult;
import net.dv8tion.jda.api.events.Event;
public interface Consumable {
default boolean shouldConsume(Event event, ExecutionResult result) { return false; }
}

View File

@@ -1,7 +0,0 @@
package dev.sheldan.abstracto.core.listener;
import net.dv8tion.jda.api.entities.Message;
public interface MessageReceivedListener extends FeatureAware, Prioritized {
void execute(Message message);
}

View File

@@ -1,9 +0,0 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
public interface ReactedAddedListener extends FeatureAware, Prioritized {
void executeReactionAdded(CachedMessage message, GuildMessageReactionAddEvent event, AUserInAServer userAdding);
}

View File

@@ -1,9 +0,0 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveEvent;
public interface ReactedRemovedListener extends FeatureAware, Prioritized {
void executeReactionRemoved(CachedMessage message, GuildMessageReactionRemoveEvent reaction, AUserInAServer userRemoving);
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
public interface AsyncEmoteCreatedListener extends FeatureAware, Prioritized {
void emoteCreated(CachedEmote createdEmote);
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
public interface AsyncEmoteDeletedListener extends FeatureAware, Prioritized {
void emoteDeleted(CachedEmote deletedEmote);
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
public interface AsyncEmoteUpdatedListener extends FeatureAware, Prioritized {
void emoteUpdated(CachedEmote updatedEmote, String oldValue, String newValue);
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.models.ServerUser;
public interface AsyncJoinListener extends FeatureAware {
void execute(ServerUser joiningUser);
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.models.ServerUser;
public interface AsyncLeaveListener extends FeatureAware {
void execute(ServerUser serverUser);
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
public interface AsyncMessageDeletedListener extends FeatureAware {
void execute(CachedMessage messageBefore);
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.models.listener.GuildMessageEmbedEventModel;
public interface AsyncMessageEmbeddedListener extends FeatureAware {
void execute(GuildMessageEmbedEventModel eventModel);
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
public interface AsyncMessageReceivedListener extends FeatureAware {
void execute(CachedMessage message);
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
public interface AsyncMessageTextUpdatedListener extends FeatureAware {
void execute(CachedMessage messageBefore, CachedMessage messageAfter);
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
public interface AsyncPrivateMessageReceivedListener extends FeatureAware {
void execute(CachedMessage message);
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
public interface AsyncReactionAddedListener extends FeatureAware {
void executeReactionAdded(CachedMessage message, CachedReactions addedReaction, ServerUser userAdding);
}

View File

@@ -0,0 +1,8 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
public interface AsyncReactionClearedListener extends FeatureAware {
void executeReactionCleared(CachedMessage message);
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.core.listener.async.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
public interface AsyncReactionRemovedListener extends FeatureAware {
void executeReactionRemoved(CachedMessage message, CachedReactions removedReaction, ServerUser userRemoving);
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener.entity;
package dev.sheldan.abstracto.core.listener.sync.entity;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener.entity;
package dev.sheldan.abstracto.core.listener.sync.entity;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.entity;
import dev.sheldan.abstracto.core.models.database.AServer;

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import net.dv8tion.jda.api.entities.Emote;
public interface EmoteCreatedListener extends FeatureAware, Prioritized {

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import net.dv8tion.jda.api.entities.Emote;
public interface EmoteDeletedListener extends FeatureAware, Prioritized {

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import net.dv8tion.jda.api.entities.Emote;
public interface EmoteUpdatedListener extends FeatureAware, Prioritized {

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import dev.sheldan.abstracto.core.models.listener.GuildMessageEmbedEventModel;
public interface MessageEmbeddedListener extends FeatureAware, Prioritized {

View File

@@ -0,0 +1,11 @@
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.listener.Consumable;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import dev.sheldan.abstracto.core.execution.result.MessageReceivedListenerResult;
import net.dv8tion.jda.api.entities.Message;
public interface MessageReceivedListener extends FeatureAware, Prioritized, Consumable {
MessageReceivedListenerResult execute(Message message);
}

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import net.dv8tion.jda.api.entities.Message;

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import net.dv8tion.jda.api.entities.Message;
public interface PrivateMessageReceivedListener extends FeatureAware, Prioritized {

View File

@@ -0,0 +1,11 @@
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
public interface ReactionAddedListener extends FeatureAware, Prioritized {
void executeReactionAdded(CachedMessage message, GuildMessageReactionAddEvent event, ServerUser serverUser);
}

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.core.listener;
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
public interface ReactionClearedListener extends FeatureAware, Prioritized {

View File

@@ -0,0 +1,11 @@
package dev.sheldan.abstracto.core.listener.sync.jda;
import dev.sheldan.abstracto.core.FeatureAware;
import dev.sheldan.abstracto.core.Prioritized;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionRemoveEvent;
public interface ReactionRemovedListener extends FeatureAware, Prioritized {
void executeReactionRemoved(CachedMessage message, GuildMessageReactionRemoveEvent reaction, ServerUser serverUser);
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.core.models;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
@EqualsAndHashCode
public class ServerUser {
private Long serverId;
private Long userId;
}

View File

@@ -0,0 +1,17 @@
package dev.sheldan.abstracto.core.models.cache;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class CachedAttachment {
private String url;
private String proxyUrl;
private String fileName;
private Integer size;
private Integer height;
private Integer width;
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.core.models.cache;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class CachedAuthor {
private Long authorId;
private Boolean isBot;
}

View File

@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.core.models.cache;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.EmbedType;
import java.time.OffsetDateTime;
import java.util.List;
@@ -15,9 +16,10 @@ public class CachedEmbed {
private CachedEmbedTitle title;
private CachedEmbedColor color;
private String description;
private String thumbnail;
private String imageUrl;
private CachedThumbnail cachedThumbnail;
private CachedImageInfo cachedImageInfo;
private List<CachedEmbedField> fields;
private CachedEmbedFooter footer;
private OffsetDateTime timeStamp;
private EmbedType type;
}

Some files were not shown because too many files have changed in this diff Show More