diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MessageDeletedListener.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MessageDeletedListener.java index 7610eaa0d..ed1ada5d0 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MessageDeletedListener.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MessageDeletedListener.java @@ -39,16 +39,25 @@ public class MessageDeletedListener extends ListenerAdapter { @Autowired private PostTargetService postTargetService; + @Autowired + private MessageDeletedListener self; + @Override @Transactional public void onGuildMessageDelete(@Nonnull GuildMessageDeleteEvent event) { - CachedMessage messageFromCache = null; - try { - messageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong()); - } catch (ExecutionException | InterruptedException e) { - log.warn("Failed to load message.", e); - return; - } + messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong()) + .thenAccept(messageFromCache -> { + try { + self.logMessage(event, messageFromCache); + } catch (Exception e) { + log.error("Error when logging message deletion.", e); + } + }); + + } + + @Transactional + public void logMessage(@Nonnull GuildMessageDeleteEvent event, CachedMessage messageFromCache) { MessageDeletedLog logModel = (MessageDeletedLog) contextUtils.fromMessage(messageFromCache, MessageDeletedLog.class); logModel.setMessage(messageFromCache); String simpleMessageUpdatedMessage = templateService.renderTemplate(MESSAGE_DELETED_TEMPLATE, logModel); diff --git a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MessageEditedListener.java b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MessageEditedListener.java index 1ba67d37b..d0bd07778 100644 --- a/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MessageEditedListener.java +++ b/abstracto-application/abstracto-modules/moderation/moderation-impl/src/main/java/dev/sheldan/abstracto/moderation/listener/MessageEditedListener.java @@ -1,6 +1,6 @@ package dev.sheldan.abstracto.moderation.listener; -import dev.sheldan.abstracto.core.MessageTextUpdatedListener; +import dev.sheldan.abstracto.core.listener.MessageTextUpdatedListener; import dev.sheldan.abstracto.core.models.CachedMessage; import dev.sheldan.abstracto.core.models.embed.MessageToSend; import dev.sheldan.abstracto.core.service.MessageCache; diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/command/StarStats.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/command/StarStats.java new file mode 100644 index 000000000..498f957d9 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/command/StarStats.java @@ -0,0 +1,51 @@ +package dev.sheldan.abstracto.utility.command; + +import dev.sheldan.abstracto.command.Command; +import dev.sheldan.abstracto.command.HelpInfo; +import dev.sheldan.abstracto.command.execution.CommandConfiguration; +import dev.sheldan.abstracto.command.execution.CommandContext; +import dev.sheldan.abstracto.command.execution.Parameter; +import dev.sheldan.abstracto.command.execution.Result; +import dev.sheldan.abstracto.core.models.embed.MessageToSend; +import dev.sheldan.abstracto.templating.TemplateService; +import dev.sheldan.abstracto.utility.Utility; +import dev.sheldan.abstracto.utility.models.template.starboard.StarStatsModel; +import dev.sheldan.abstracto.utility.service.StarboardService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class StarStats implements Command { + + public static final String STARSTATS_RESPONSE_TEMPLATE = "starStats_response"; + @Autowired + private StarboardService starboardService; + + @Autowired + private TemplateService templateService; + + @Override + public Result execute(CommandContext commandContext) { + StarStatsModel result = starboardService.retrieveStarStats(commandContext.getGuild().getIdLong()); + MessageToSend messageToSend = templateService.renderEmbedTemplate(STARSTATS_RESPONSE_TEMPLATE, result); + commandContext.getChannel().sendMessage(messageToSend.getEmbed()).queue(); + return Result.fromSuccess(); + } + + @Override + public CommandConfiguration getConfiguration() { + List parameters = new ArrayList<>(); + HelpInfo helpInfo = HelpInfo.builder().templated(true).build(); + return CommandConfiguration.builder() + .name("starStats") + .module(Utility.UTILITY) + .templated(true) + .causesReaction(false) + .parameters(parameters) + .help(helpInfo) + .build(); + } +} diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/config/StarboardConfig.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/config/StarboardConfig.java new file mode 100644 index 000000000..878a552a5 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/config/StarboardConfig.java @@ -0,0 +1,18 @@ +package dev.sheldan.abstracto.utility.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +@Getter +@Setter +@ConfigurationProperties(prefix = "abstracto.starboard") +public class StarboardConfig { + private List lvl = new ArrayList<>(); + private List badge = new ArrayList<>(); +} diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/config/StarboardConfigListener.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/config/StarboardConfigListener.java new file mode 100644 index 000000000..0e8ad89f2 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/config/StarboardConfigListener.java @@ -0,0 +1,31 @@ +package dev.sheldan.abstracto.utility.config; + +import dev.sheldan.abstracto.core.listener.ServerConfigListener; +import dev.sheldan.abstracto.core.management.ServerManagementService; +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.management.ConfigManagementService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +@Slf4j +public class StarboardConfigListener implements ServerConfigListener { + + @Autowired + private StarboardConfig starboardConfig; + + @Autowired + private ConfigManagementService configManagementService; + + @Override + public void updateServerConfig(AServer server) { + for (int i = 0; i < starboardConfig.getLvl().size(); i++) { + Integer value = starboardConfig.getLvl().get(i); + configManagementService.createIfNotExists(server.getId(), "starLvl" + ( i + 1 ), Double.valueOf(value)); + } + } +} diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/listener/StarboardListener.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/listener/StarboardListener.java new file mode 100644 index 000000000..c007b7017 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/listener/StarboardListener.java @@ -0,0 +1,142 @@ +package dev.sheldan.abstracto.utility.listener; + +import dev.sheldan.abstracto.core.listener.ReactedAddedListener; +import dev.sheldan.abstracto.core.listener.ReactedRemovedListener; +import dev.sheldan.abstracto.core.management.EmoteManagementService; +import dev.sheldan.abstracto.core.management.UserManagementService; +import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.CachedReaction; +import dev.sheldan.abstracto.core.models.database.AEmote; +import dev.sheldan.abstracto.core.models.database.AUser; +import dev.sheldan.abstracto.core.models.database.AUserInAServer; +import dev.sheldan.abstracto.core.service.Bot; +import dev.sheldan.abstracto.core.management.ConfigManagementService; +import dev.sheldan.abstracto.core.service.MessageCache; +import dev.sheldan.abstracto.core.utils.EmoteUtils; +import dev.sheldan.abstracto.utility.models.StarboardPost; +import dev.sheldan.abstracto.utility.service.StarboardService; +import dev.sheldan.abstracto.utility.service.management.StarboardPostManagementService; +import dev.sheldan.abstracto.utility.service.management.StarboardPostReactorManagementService; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Emote; +import net.dv8tion.jda.api.entities.MessageReaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Component +@Slf4j +public class StarboardListener implements ReactedAddedListener, ReactedRemovedListener { + + public static final String STAR_EMOTE = "STAR"; + + @Autowired + private EmoteManagementService emoteManagementService; + + @Autowired + private Bot bot; + + @Autowired + private MessageCache messageCache; + + @Autowired + private ConfigManagementService configManagementService; + + @Autowired + private StarboardService starboardService; + + @Autowired + private StarboardPostManagementService starboardPostManagementService; + + @Autowired + private StarboardPostReactorManagementService starboardPostReactorManagementService; + + @Autowired + private UserManagementService userManagementService; + + @Override + @Transactional + public void executeReactionAdded(CachedMessage message, MessageReaction addedReaction, AUserInAServer userAdding) { + if(userAdding.getUserReference().getId().equals(message.getAuthorId())) { + return; + } + Long guildId = message.getServerId(); + Optional aEmote = emoteManagementService.loadEmoteByName(STAR_EMOTE, guildId); + if(aEmote.isPresent()) { + AEmote emote = aEmote.get(); + MessageReaction.ReactionEmote reactionEmote = addedReaction.getReactionEmote(); + Emote emoteInGuild = bot.getEmote(guildId, emote); + if(EmoteUtils.isReactionEmoteAEmote(reactionEmote, emote, Optional.ofNullable(emoteInGuild))) { + Optional reactionOptional = EmoteUtils.getReactionFromMessageByEmote(message, emote); + updateStarboardPost(message, reactionOptional.orElse(null), userAdding, true); + } + } else { + log.warn("Emote {} is not defined for guild {}. Starboard not functional.", STAR_EMOTE, guildId); + } + } + + private void updateStarboardPost(CachedMessage message, CachedReaction reaction, AUserInAServer userReacting, boolean adding) { + Optional starboardPostOptional = starboardPostManagementService.findByMessageId(message.getMessageId()); + if(reaction != null) { + List userExceptAuthor = getUsersExcept(reaction.getUsers(), message.getAuthorId()); + Double starMinimum = getFromConfig("starLvl1", message.getServerId()); + if (userExceptAuthor.size() >= starMinimum) { + AUserInAServer author = userManagementService.loadUser(message.getServerId(), message.getAuthorId()); + if(starboardPostOptional.isPresent()) { + StarboardPost starboardPost = starboardPostOptional.get(); + starboardService.updateStarboardPost(starboardPost, message, userExceptAuthor); + if(adding) { + starboardPostReactorManagementService.addReactor(starboardPost, userReacting.getUserReference()); + } else { + starboardPostReactorManagementService.removeReactor(starboardPost, userReacting.getUserReference()); + } + } else { + starboardService.createStarboardPost(message, userExceptAuthor, userReacting, author); + } + } else { + starboardPostOptional.ifPresent(starboardPost -> { + starboardService.removeStarboardPost(starboardPost); + starboardPostReactorManagementService.removeReactors(starboardPost); + }); + } + } else { + starboardPostOptional.ifPresent(starboardPost -> { + starboardService.removeStarboardPost(starboardPost); + starboardPostReactorManagementService.removeReactors(starboardPost); + }); + } + } + + @Override + @Transactional + public void executeReactionRemoved(CachedMessage message, MessageReaction removedReaction, AUserInAServer userRemoving) { + if(message.getAuthorId().equals(userRemoving.getUserReference().getId())) { + return; + } + Long guildId = message.getServerId(); + Optional aEmote = emoteManagementService.loadEmoteByName(STAR_EMOTE, guildId); + if(aEmote.isPresent()) { + AEmote emote = aEmote.get(); + MessageReaction.ReactionEmote reactionEmote = removedReaction.getReactionEmote(); + Emote emoteInGuild = bot.getEmote(guildId, emote); + if(EmoteUtils.isReactionEmoteAEmote(reactionEmote, emote, Optional.ofNullable(emoteInGuild))) { + Optional reactionOptional = EmoteUtils.getReactionFromMessageByEmote(message, emote); + updateStarboardPost(message, reactionOptional.orElse(null), userRemoving, false); + } + } else { + log.warn("Emote {} is not defined for guild {}. Starboard not functional.", STAR_EMOTE, guildId); + } + } + + private Double getFromConfig(String key, Long guildId) { + return configManagementService.loadConfig(guildId, key).getDoubleValue(); + } + + private List getUsersExcept(List users, Long userId) { + return users.stream().filter(user -> !user.getId().equals(userId)).collect(Collectors.toList()); + } +} diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/StarStatsUserResult.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/StarStatsUserResult.java new file mode 100644 index 000000000..ebf1cb07e --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/StarStatsUserResult.java @@ -0,0 +1,6 @@ +package dev.sheldan.abstracto.utility.repository; + +public interface StarStatsUserResult { + Long getUserId(); + Integer getStarCount(); +} diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/StarboardPostReactionRepository.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/StarboardPostReactionRepository.java new file mode 100644 index 000000000..e01667854 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/StarboardPostReactionRepository.java @@ -0,0 +1,42 @@ +package dev.sheldan.abstracto.utility.repository; + +import dev.sheldan.abstracto.core.models.database.AUser; +import dev.sheldan.abstracto.utility.models.StarboardPost; +import dev.sheldan.abstracto.utility.models.StarboardPostReaction; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface StarboardPostReactionRepository extends JpaRepository { + void deleteByReactorAndStarboardPost(AUser user, StarboardPost post); + void deleteByStarboardPost(StarboardPost post); + + @Query(value = "SELECT r.reactor_id as userId, COUNT(*) AS starCount \n" + + "FROM starboard_post_reaction r \n" + + "INNER JOIN starboard_post p ON p.id = r.post_id\n" + + "INNER JOIN channel c ON c.id = p.channel_id\n" + + "WHERE c.server_id = :serverId\n" + + "GROUP BY r.reactor_id \n" + + "ORDER BY starCount DESC \n" + + "LIMIT :count", nativeQuery = true) + List findTopStarGiverInServer(Long serverId, Integer count); + + @Query(value = "SELECT COUNT(*) \n" + + "FROM starboard_post_reaction r \n" + + "INNER JOIN starboard_post p ON p.id = r.post_id\n" + + "INNER JOIN channel c ON c.id = p.channel_id\n" + + "WHERE c.server_id = :serverId\n" + , nativeQuery = true) + Integer getReactionCountByServer(Long serverId); + + @Query(value = "SELECT p.poster as userId, COUNT(*) AS starCount \n" + + "FROM starboard_post_reaction r \n" + + "INNER JOIN starboard_post p ON p.id = r.post_id\n" + + "INNER JOIN channel c ON c.id = p.channel_id\n" + + "WHERE c.server_id = :serverId\n" + + "GROUP BY p.poster \n" + + "ORDER BY starCount DESC \n" + + "LIMIT :count", nativeQuery = true) + List retrieveTopStarReceiverInServer(Long serverId, Integer count); +} diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/StarboardPostRepository.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/StarboardPostRepository.java new file mode 100644 index 000000000..1112de095 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/StarboardPostRepository.java @@ -0,0 +1,12 @@ +package dev.sheldan.abstracto.utility.repository; + +import dev.sheldan.abstracto.utility.models.StarboardPost; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface StarboardPostRepository extends JpaRepository { + StarboardPost findByPostMessageId(Long messageId); + List findByStarboardChannelServerId(Long serverId); + +} diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/converter/StarStatsUserConverter.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/converter/StarStatsUserConverter.java new file mode 100644 index 000000000..90bfd4d6b --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/repository/converter/StarStatsUserConverter.java @@ -0,0 +1,36 @@ +package dev.sheldan.abstracto.utility.repository.converter; + +import dev.sheldan.abstracto.core.management.UserManagementService; +import dev.sheldan.abstracto.core.models.database.AUser; +import dev.sheldan.abstracto.core.service.Bot; +import dev.sheldan.abstracto.utility.models.template.starboard.StarStatsUser; +import dev.sheldan.abstracto.utility.repository.StarStatsUserResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +public class StarStatsUserConverter { + + @Autowired + private UserManagementService userManagementService; + + @Autowired + private Bot bot; + + public List convertToStarStatsUser(List users, Long serverId) { + List result = new ArrayList<>(); + users.forEach(starStatsUserResult -> { + StarStatsUser newUser = StarStatsUser + .builder() + .starCount(starStatsUserResult.getStarCount()) + .member(bot.getMemberInServer(serverId, starStatsUserResult.getUserId())) + .user(AUser.builder().id(starStatsUserResult.getUserId()).build()) + .build(); + result.add(newUser); + }); + return result; + } +} diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/RemindServiceBean.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/RemindServiceBean.java index a8c09a5ae..e611f799d 100644 --- a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/RemindServiceBean.java +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/RemindServiceBean.java @@ -17,6 +17,7 @@ import dev.sheldan.abstracto.utility.service.management.ReminderManagementServic import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.TextChannel; +import org.apache.commons.lang3.StringUtils; import org.quartz.JobDataMap; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -66,7 +67,12 @@ public class RemindServiceBean implements ReminderService { Reminder reminder = reminderManagementService.createReminder(aServerAChannelAUser, remindText, remindAt, reminderModel.getMessage().getIdLong()); reminderModel.setReminder(reminder); MessageToSend message = templateService.renderEmbedTemplate(REMINDER_EMBED_KEY, reminderModel); - reminderModel.getTextChannel().sendMessage(message.getMessage()).embed(message.getEmbed()).queue(); + String messageText = message.getMessage(); + if(StringUtils.isBlank(messageText)) { + reminderModel.getTextChannel().sendMessage(message.getEmbed()).queue(); + } else { + reminderModel.getTextChannel().sendMessage(messageText).embed(message.getEmbed()).queue(); + } if(remindIn.getSeconds() < 60) { ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/StarboardServiceBean.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/StarboardServiceBean.java new file mode 100644 index 000000000..7df08fba9 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/StarboardServiceBean.java @@ -0,0 +1,181 @@ +package dev.sheldan.abstracto.utility.service; + +import dev.sheldan.abstracto.core.management.EmoteManagementService; +import dev.sheldan.abstracto.core.management.PostTargetManagement; +import dev.sheldan.abstracto.core.management.UserManagementService; +import dev.sheldan.abstracto.core.models.AServerChannelMessage; +import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.database.*; +import dev.sheldan.abstracto.core.models.embed.MessageToSend; +import dev.sheldan.abstracto.core.service.Bot; +import dev.sheldan.abstracto.core.service.ConfigService; +import dev.sheldan.abstracto.core.service.EmoteService; +import dev.sheldan.abstracto.core.service.PostTargetService; +import dev.sheldan.abstracto.templating.TemplateService; +import dev.sheldan.abstracto.utility.config.StarboardConfig; +import dev.sheldan.abstracto.utility.models.StarboardPost; +import dev.sheldan.abstracto.utility.models.template.starboard.StarStatsModel; +import dev.sheldan.abstracto.utility.models.template.starboard.StarStatsPost; +import dev.sheldan.abstracto.utility.models.template.starboard.StarStatsUser; +import dev.sheldan.abstracto.utility.models.template.starboard.StarboardPostModel; +import dev.sheldan.abstracto.utility.service.management.StarboardPostManagementService; +import dev.sheldan.abstracto.utility.service.management.StarboardPostReactorManagementService; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + + +@Component +@Slf4j +public class StarboardServiceBean implements StarboardService { + + @Autowired + private Bot bot; + + @Autowired + private PostTargetService postTargetService; + + @Autowired + private TemplateService templateService; + + @Autowired + private EmoteManagementService emoteManagementService; + + @Autowired + private ConfigService configService; + + @Autowired + private StarboardPostManagementService starboardPostManagementService; + + @Autowired + private StarboardConfig starboardConfig; + + @Autowired + private UserManagementService userManagementService; + + @Autowired + private StarboardPostReactorManagementService starboardPostReactorManagementService; + + @Autowired + private PostTargetManagement postTargetManagement; + + @Autowired + private EmoteService emoteService; + + @Override + public void createStarboardPost(CachedMessage message, List userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser) { + StarboardPostModel starboardPostModel = buildStarboardPostModel(message, userExceptAuthor.size()); + MessageToSend messageToSend = templateService.renderEmbedTemplate("starboard_post", starboardPostModel); + PostTarget starboard = postTargetManagement.getPostTarget("starboard", message.getServerId()); + postTargetService.sendEmbedInPostTarget(messageToSend, "starboard", message.getServerId()).thenAccept(message1 -> { + AServerChannelMessage aServerChannelMessage = AServerChannelMessage + .builder() + .messageId(message1.getIdLong()) + .channel(starboard.getChannelReference()) + .server(userReacting.getServerReference()) + .build(); + StarboardPost starboardPost = starboardPostManagementService.createStarboardPost(message, starredUser, userReacting, aServerChannelMessage); + // TODO maybe in bulk, but numbers should be small enough + userExceptAuthor.forEach(user -> { + starboardPostReactorManagementService.addReactor(starboardPost, user); + }); + }); + + } + + private StarboardPostModel buildStarboardPostModel(CachedMessage message, Integer starCount) { + Member member = bot.getMemberInServer(message.getServerId(), message.getAuthorId()); + TextChannel channel = bot.getTextChannelFromServer(message.getServerId(), message.getChannelId()); + Guild guild = bot.getGuildById(message.getServerId()); + AChannel aChannel = AChannel.builder().id(message.getChannelId()).build(); + AUser user = AUser.builder().id(message.getAuthorId()).build(); + AServer server = AServer.builder().id(message.getServerId()).build(); + Optional appropriateEmoteOptional = getAppropriateEmote(message.getServerId(), starCount); + String emoteText; + if(appropriateEmoteOptional.isPresent()) { + AEmote emote = appropriateEmoteOptional.get(); + emoteText = emoteService.getEmoteAsMention(emote, message.getServerId(), "⭐"); + } else { + log.warn("No emote defined to be used for starboard post. Falling back to default."); + emoteText = "⭐"; + } + return StarboardPostModel + .builder() + .message(message) + .author(member) + .channel(channel) + .aChannel(aChannel) + .starCount(starCount) + .guild(guild) + .user(user) + .server(server) + .starLevelEmote(emoteText) + .build(); + } + + @Override + public void updateStarboardPost(StarboardPost post, CachedMessage message, List userExceptAuthor) { + StarboardPostModel starboardPostModel = buildStarboardPostModel(message, userExceptAuthor.size()); + MessageToSend messageToSend = templateService.renderEmbedTemplate("starboard_post", starboardPostModel); + CompletableFuture future = new CompletableFuture<>(); + postTargetService.editOrCreatedInPostTarget(post.getStarboardMessageId(), messageToSend, "starboard", message.getServerId(), future); + future.thenAccept(newOrOldMessage -> { + starboardPostManagementService.setStarboardPostMessageId(post, newOrOldMessage.getIdLong()); + }); + } + + @Override + public void removeStarboardPost(StarboardPost message) { + AChannel starboardChannel = message.getStarboardChannel(); + bot.deleteMessage(starboardChannel.getServer().getId(), starboardChannel.getId(), message.getStarboardMessageId()); + } + + @Override + public StarStatsModel retrieveStarStats(Long serverId) { + int count = 3; + List starboardPosts = starboardPostManagementService.retrieveTopPosts(serverId, count); + List topStarGivers = starboardPostReactorManagementService.retrieveTopStarGiver(serverId, count); + List starStatsPosts = starboardPosts.stream().map(StarStatsPost::fromStarboardPost).collect(Collectors.toList()); + List topStarReceiver = starboardPostReactorManagementService.retrieveTopStarReceiver(serverId, count); + Integer postCount = starboardPostManagementService.getPostCount(serverId); + Integer reactionCount = starboardPostReactorManagementService.getStarCount(serverId); + List emotes = new ArrayList<>(); + for (int i = 1; i < count + 1; i++) { + Optional starboardRankingEmote = getStarboardRankingEmote(serverId, i); + AEmote emote = starboardRankingEmote.orElse(null); + String defaultEmoji = starboardConfig.getBadge().get(i - 1); + emotes.add(emoteService.getEmoteAsMention(emote, serverId, defaultEmoji)); + } + + return StarStatsModel + .builder() + .badgeEmotes(emotes) + .starGiver(topStarGivers) + .starReceiver(topStarReceiver) + .topPosts(starStatsPosts) + .starredMessages(postCount) + .totalStars(reactionCount) + .build(); + } + + private Optional getStarboardRankingEmote(Long serverId, Integer position) { + return emoteManagementService.loadEmoteByName("starboardBadge" + position, serverId); + } + + private Optional getAppropriateEmote(Long serverId, Integer starCount) { + for(int i = starboardConfig.getLvl().size(); i > 0; i--) { + Double starMinimum = configService.getDoubleValue("starLvl" + i, serverId); + if(starCount >= starMinimum) { + return emoteManagementService.loadEmoteByName("star" + i, serverId); + } + } + return emoteManagementService.loadEmoteByName("star0", serverId); + } +} diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/SuggestionServiceBean.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/SuggestionServiceBean.java index 489ea0b5a..bd70a67d4 100644 --- a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/SuggestionServiceBean.java +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/SuggestionServiceBean.java @@ -27,8 +27,8 @@ import java.util.Optional; public class SuggestionServiceBean implements SuggestionService { public static final String SUGGESTION_LOG_TEMPLATE = "suggest_log"; - private static final String SUGGESTION_YES_EMOTE = "SUGGESTION_YES"; - private static final String SUGGESTION_NO_EMOTE = "SUGGESTION_NO"; + private static final String SUGGESTION_YES_EMOTE = "suggestionYes"; + private static final String SUGGESTION_NO_EMOTE = "suggestionNo"; public static final String SUGGESTIONS_TARGET = "suggestions"; @Autowired private SuggestionManagementService suggestionManagementService; diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostManagementServiceBean.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostManagementServiceBean.java new file mode 100644 index 000000000..d9da76114 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostManagementServiceBean.java @@ -0,0 +1,72 @@ +package dev.sheldan.abstracto.utility.service.management; + +import dev.sheldan.abstracto.core.models.AServerChannelMessage; +import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.database.AUserInAServer; +import dev.sheldan.abstracto.utility.models.StarboardPost; +import dev.sheldan.abstracto.utility.repository.StarboardPostRepository; +import dev.sheldan.abstracto.utility.repository.converter.StarStatsUserConverter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +@Component +public class StarboardPostManagementServiceBean implements StarboardPostManagementService { + + @Autowired + private StarboardPostRepository repository; + + @Autowired + private StarStatsUserConverter converter; + + @Override + public StarboardPost createStarboardPost(CachedMessage starredMessage, AUserInAServer starredUser, AUserInAServer starringUser, AServerChannelMessage starboardPost) { + StarboardPost post = StarboardPost + .builder() + .author(starredUser.getUserReference()) + .deleted(false) + .postMessageId(starredMessage.getMessageId()) + .starboardMessageId(starboardPost.getMessageId()) + .starboardChannel(starboardPost.getChannel()) + .starredDate(Instant.now()) + .build(); + repository.save(post); + return post; + } + + @Override + public void setStarboardPostMessageId(StarboardPost post, Long messageId) { + post.setStarboardMessageId(messageId); + repository.save(post); + } + + @Override + public List retrieveTopPosts(Long serverId, Integer count) { + List posts = retrieveAllPosts(serverId); + posts.sort(Comparator.comparingInt(o -> o.getReactions().size())); + Collections.reverse(posts); + return posts.subList(0, Math.min(count, posts.size())); + } + + @Override + public List retrieveAllPosts(Long serverId) { + return repository.findByStarboardChannelServerId(serverId); + } + + @Override + public Integer getPostCount(Long serverId) { + return retrieveAllPosts(serverId).size(); + } + + @Override + public Optional findByMessageId(Long messageId) { + return Optional.ofNullable(repository.findByPostMessageId(messageId)); + } + + +} diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostReactorManagementServiceBean.java b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostReactorManagementServiceBean.java new file mode 100644 index 000000000..a0d5cf07e --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostReactorManagementServiceBean.java @@ -0,0 +1,63 @@ +package dev.sheldan.abstracto.utility.service.management; + +import dev.sheldan.abstracto.core.models.database.AUser; +import dev.sheldan.abstracto.utility.models.StarboardPost; +import dev.sheldan.abstracto.utility.models.StarboardPostReaction; +import dev.sheldan.abstracto.utility.models.template.starboard.StarStatsUser; +import dev.sheldan.abstracto.utility.repository.StarStatsUserResult; +import dev.sheldan.abstracto.utility.repository.StarboardPostReactionRepository; +import dev.sheldan.abstracto.utility.repository.converter.StarStatsUserConverter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +@Component +public class StarboardPostReactorManagementServiceBean implements StarboardPostReactorManagementService { + + @Autowired + private StarboardPostReactionRepository repository; + + @Autowired + private StarStatsUserConverter converter; + + @Override + public void addReactor(StarboardPost post, AUser user) { + StarboardPostReaction reactor = StarboardPostReaction + .builder() + .starboardPost(post) + .reactor(user) + .build(); + repository.save(reactor); + } + + @Override + public void removeReactor(StarboardPost post, AUser user) { + repository.deleteByReactorAndStarboardPost(user, post); + } + + @Override + public void removeReactors(StarboardPost post) { + repository.deleteByStarboardPost(post); + } + + @Override + public Integer getStarCount(Long serverId) { + return repository.getReactionCountByServer(serverId); + } + + @Override + public List retrieveTopStarGiver(Long serverId, Integer count) { + List starGivers = repository.findTopStarGiverInServer(serverId, count); + return converter.convertToStarStatsUser(starGivers, serverId); + } + + @Override + public List retrieveTopStarReceiver(Long serverId, Integer count) { + List starReceivers = repository.retrieveTopStarReceiverInServer(serverId, count); + return converter.convertToStarStatsUser(starReceivers, serverId); + } + +} diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_description_en_US.ftl b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_description_en_US.ftl new file mode 100644 index 000000000..1fd87c5a2 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_description_en_US.ftl @@ -0,0 +1 @@ +Shows the current leaderboard of starboard posts. \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_long_help_en_US.ftl b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_long_help_en_US.ftl new file mode 100644 index 000000000..1fd87c5a2 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_long_help_en_US.ftl @@ -0,0 +1 @@ +Shows the current leaderboard of starboard posts. \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_response_embed_en_US.ftl b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_response_embed_en_US.ftl new file mode 100644 index 000000000..580e0f065 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_response_embed_en_US.ftl @@ -0,0 +1,51 @@ +{ + "title": { + "title": "Server starboard stats" + }, + "color" : { + "r": 200, + "g": 0, + "b": 255 + }, + "description": "${starredMessages} starred messages with ${totalStars} stars in total", + "fields": [ + { + "name": "Top starred posts", + "value": " + <#list topPosts as post> + ${badgeEmotes[post?index]} - ${post.starCount} :star: [Jump!](${post.messageUrl}) + <#else> + No starred messages. + + " + }, + { + "name": "Top starrer", + "value": " + <#list starGiver as starrer> + <#if starrer.member?has_content> + ${badgeEmotes[starrer?index]} - ${starrer.starCount} :star: ${starrer.member.asMention} + <#else> + ${badgeEmotes[starrer?index]} - ${starrer.starCount} :star: ${starrer.user.id?c} (Left the guild) + + <#else> + No starred messages. + + " + }, + { + "name": "Top star receiver", + "value": " + <#list starReceiver as starred> + <#if starred.member?has_content> + ${badgeEmotes[starred?index]} - ${starred.starCount} :star: ${starred.member.asMention} + <#else> + ${badgeEmotes[starred?index]} - ${starred.starCount} :star: ${starred.user.id?c} (Left the guild) + + <#else> + No starred messages. + + " + } + ] +} \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_usage_en_US.ftl b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_usage_en_US.ftl new file mode 100644 index 000000000..03c516016 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/commands/starStats/starStats_usage_en_US.ftl @@ -0,0 +1 @@ +starStats \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/listener/starboard/starboard_post_embed_en_US.ftl b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/listener/starboard/starboard_post_embed_en_US.ftl new file mode 100644 index 000000000..0aec36100 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/templates/listener/starboard/starboard_post_embed_en_US.ftl @@ -0,0 +1,44 @@ +{ + "author": { + <#if author?has_content> + "name": "${author.effectiveName}", + "avatar": "${author.user.effectiveAvatarUrl}" + <#else> + "name": "${user.id?c} (Has left the server)" + + }, + "color" : { + "r": 200, + "g": 0, + "b": 255 + }, + <#if message.content?has_content || message.embeds?size gt 0> + "description": "${message.content} + <#list message.embeds> + Embeds: + <#items as embed> + Description: ${embed.description} <#if embed.imageUrl?has_content> ImageUrl: ${embed.imageUrl} + + + ", + + <#if channel?has_content> + "additionalMessage": "${starLevelEmote} ${starCount} ${channel.asMention} ID: ${message.messageId?c}", + <#else> + "additionalMessage": "${starLevelEmote} ${starCount} ${aChannel.id?c} ID: ${message.messageId?c}", + + <#if message.attachmentUrls?size gt 0> + "imageUrl": "${message.attachmentUrls[0]}", + + "fields": [ + { + "name": "Original", + <#if channel?has_content> + "value": "[${channel.name}](${message.messageUrl})" + <#else> + "value": "[${aChannel.id?c}](${message.messageUrl})" + + } + ], + "timeStamp": "${message.timeCreated}" +} \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/utility.properties b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/utility.properties index 201541bc3..ed5efa196 100644 --- a/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/utility.properties +++ b/abstracto-application/abstracto-modules/utility/utility-impl/src/main/resources/utility.properties @@ -1,8 +1,16 @@ -abstracto.postTargets.utility=suggestions -abstracto.emoteNames.suggestion=SUGGESTION_YES,SUGGESTION_NO +abstracto.postTargets.utility=suggestions,starboard +abstracto.emoteNames.suggestion=suggestionYes,suggestionNo +abstracto.emoteNames.starboard=star,star1,star2,star3,star4,starboardBadge1,starboardBadge2,starboardBadge3 +abstracto.starboard.lvl[0]=5 +abstracto.starboard.lvl[1]=8 +abstracto.starboard.lvl[2]=13 +abstracto.starboard.lvl[3]=17 +abstracto.starboard.badge[0]=\ud83e\udd47 +abstracto.starboard.badge[1]=\ud83e\udd48 +abstracto.starboard.badge[2]=\ud83e\udd49 abstracto.scheduling.jobs.reminderJob.name=reminderJob abstracto.scheduling.jobs.reminderJob.group=utility abstracto.scheduling.jobs.reminderJob.clazz=dev.sheldan.abstracto.utility.jobs.ReminderJob abstracto.scheduling.jobs.reminderJob.standAlone=false -abstracto.scheduling.jobs.reminderJob.active=true \ No newline at end of file +abstracto.scheduling.jobs.reminderJob.active=true diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/Reminder.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/Reminder.java index e15f47479..d0d7213fa 100644 --- a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/Reminder.java +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/Reminder.java @@ -13,7 +13,8 @@ import java.time.Instant; @Builder @AllArgsConstructor @NoArgsConstructor -@Getter @Setter +@Getter +@Setter public class Reminder { @Id diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/StarboardPost.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/StarboardPost.java new file mode 100644 index 000000000..ae6c727cf --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/StarboardPost.java @@ -0,0 +1,63 @@ +package dev.sheldan.abstracto.utility.models; + +import dev.sheldan.abstracto.core.models.database.AChannel; +import dev.sheldan.abstracto.core.models.database.AUser; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; +import java.util.List; + +@Entity +@Table(name="starboard_post") +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class StarboardPost { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "poster") + private AUser author; + + @Column + private Long starboardMessageId; + + @Column + private Long postMessageId; + + @ManyToOne + @JoinColumn(name = "channelId") + private AChannel starboardChannel; + + @Transient + private Integer reactionCount; + + @PostLoad + private void onLoad() { + this.reactionCount = this.reactions.size(); + } + + @Getter + @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}) + @JoinColumn(name="postId") + private List reactions; + + @Column + private Instant starredDate; + + @Column + private boolean deleted; + + public int getReactionCount() { + if(this.reactions == null) { + return 0; + } + return this.reactions.size(); + } +} diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/StarboardPostReaction.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/StarboardPostReaction.java new file mode 100644 index 000000000..dd066bb33 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/StarboardPostReaction.java @@ -0,0 +1,29 @@ +package dev.sheldan.abstracto.utility.models; + +import dev.sheldan.abstracto.core.models.database.AUser; +import lombok.*; + +import javax.persistence.*; + +@Entity +@Table(name="starboard_post_reaction") +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class StarboardPostReaction { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "reactorId") + private AUser reactor; + + @ManyToOne + @JoinColumn(name = "postId") + private StarboardPost starboardPost; + +} diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarStatsModel.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarStatsModel.java new file mode 100644 index 000000000..cd6ab2492 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarStatsModel.java @@ -0,0 +1,19 @@ +package dev.sheldan.abstracto.utility.models.template.starboard; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@Builder +public class StarStatsModel { + private List topPosts; + private List starReceiver; + private List starGiver; + private Integer totalStars; + private List badgeEmotes; + private Integer starredMessages; +} diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarStatsPost.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarStatsPost.java new file mode 100644 index 000000000..c85e12408 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarStatsPost.java @@ -0,0 +1,33 @@ +package dev.sheldan.abstracto.utility.models.template.starboard; + +import dev.sheldan.abstracto.core.models.database.AChannel; +import dev.sheldan.abstracto.core.utils.MessageUtils; +import dev.sheldan.abstracto.utility.models.StarboardPost; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +public class StarStatsPost { + private Long serverId; + private Long channelId; + private Long messageId; + private Integer starCount; + + public String getMessageUrl() { + return MessageUtils.buildMessageUrl(serverId ,channelId, messageId); + } + + public static StarStatsPost fromStarboardPost(StarboardPost starboardPost) { + AChannel channel = starboardPost.getStarboardChannel(); + return StarStatsPost + .builder() + .serverId(channel.getServer().getId()) + .channelId(channel.getId()) + .messageId(starboardPost.getPostMessageId()) + .starCount(starboardPost.getReactions().size()) + .build(); + } +} diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarStatsUser.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarStatsUser.java new file mode 100644 index 000000000..c14c20c31 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarStatsUser.java @@ -0,0 +1,19 @@ +package dev.sheldan.abstracto.utility.models.template.starboard; + +import dev.sheldan.abstracto.core.models.database.AUser; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import net.dv8tion.jda.api.entities.Member; + + +@Getter +@Setter +@Builder +public class StarStatsUser { + private AUser user; + private Member member; + private Integer starCount; + + +} diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarboardPostModel.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarboardPostModel.java new file mode 100644 index 000000000..aab64171b --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/models/template/starboard/StarboardPostModel.java @@ -0,0 +1,24 @@ +package dev.sheldan.abstracto.utility.models.template.starboard; + +import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.ServerContext; +import dev.sheldan.abstracto.core.models.database.AChannel; +import dev.sheldan.abstracto.core.models.database.AUser; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.SuperBuilder; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.TextChannel; + +@Getter +@Setter +@SuperBuilder +public class StarboardPostModel extends ServerContext { + private Member author; + private TextChannel channel; + private AUser user; + private AChannel aChannel; + private CachedMessage message; + private Integer starCount; + private String starLevelEmote; +} diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/service/StarboardService.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/service/StarboardService.java new file mode 100644 index 000000000..077eae8f9 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/service/StarboardService.java @@ -0,0 +1,16 @@ +package dev.sheldan.abstracto.utility.service; + +import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.database.AUser; +import dev.sheldan.abstracto.core.models.database.AUserInAServer; +import dev.sheldan.abstracto.utility.models.StarboardPost; +import dev.sheldan.abstracto.utility.models.template.starboard.StarStatsModel; + +import java.util.List; + +public interface StarboardService { + void createStarboardPost(CachedMessage message, List userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser); + void updateStarboardPost(StarboardPost post, CachedMessage message, List userExceptAuthor); + void removeStarboardPost(StarboardPost message); + StarStatsModel retrieveStarStats(Long serverId); +} diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostManagementService.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostManagementService.java new file mode 100644 index 000000000..76d3ceb32 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostManagementService.java @@ -0,0 +1,18 @@ +package dev.sheldan.abstracto.utility.service.management; + +import dev.sheldan.abstracto.core.models.AServerChannelMessage; +import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.database.AUserInAServer; +import dev.sheldan.abstracto.utility.models.StarboardPost; + +import java.util.List; +import java.util.Optional; + +public interface StarboardPostManagementService { + StarboardPost createStarboardPost(CachedMessage starredMessage, AUserInAServer starredUser, AUserInAServer starringUser, AServerChannelMessage starboardPost); + void setStarboardPostMessageId(StarboardPost post, Long messageId); + List retrieveTopPosts(Long serverId, Integer count); + List retrieveAllPosts(Long serverId); + Integer getPostCount(Long serverId); + Optional findByMessageId(Long messageId); +} diff --git a/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostReactorManagementService.java b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostReactorManagementService.java new file mode 100644 index 000000000..b7d3977a8 --- /dev/null +++ b/abstracto-application/abstracto-modules/utility/utility-int/src/main/java/dev/sheldan/abstracto/utility/service/management/StarboardPostReactorManagementService.java @@ -0,0 +1,16 @@ +package dev.sheldan.abstracto.utility.service.management; + +import dev.sheldan.abstracto.core.models.database.AUser; +import dev.sheldan.abstracto.utility.models.StarboardPost; +import dev.sheldan.abstracto.utility.models.template.starboard.StarStatsUser; + +import java.util.List; + +public interface StarboardPostReactorManagementService { + void addReactor(StarboardPost post, AUser user); + void removeReactor(StarboardPost post, AUser user); + void removeReactors(StarboardPost post); + Integer getStarCount(Long serverId); + List retrieveTopStarGiver(Long serverId, Integer count); + List retrieveTopStarReceiver(Long serverId, Integer count); +} diff --git a/abstracto-application/command/command-support/src/main/java/dev/sheldan/abstracto/commands/management/post/ReactionPostExecution.java b/abstracto-application/command/command-support/src/main/java/dev/sheldan/abstracto/commands/management/post/ReactionPostExecution.java index 07c42f8be..1057a58d1 100644 --- a/abstracto-application/command/command-support/src/main/java/dev/sheldan/abstracto/commands/management/post/ReactionPostExecution.java +++ b/abstracto-application/command/command-support/src/main/java/dev/sheldan/abstracto/commands/management/post/ReactionPostExecution.java @@ -18,7 +18,7 @@ public class ReactionPostExecution implements PostCommandExecution { } } else { if(command.getConfiguration().isCausesReaction()){ - commandContext.getMessage().addReaction("⭐").queue(); + commandContext.getMessage().addReaction("✅").queue(); } } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotService.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotService.java index 36af00e55..62dee7c29 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotService.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/BotService.java @@ -1,8 +1,10 @@ package dev.sheldan.abstracto.core.service; import dev.sheldan.abstracto.core.models.ServerChannelUser; +import dev.sheldan.abstracto.core.models.database.AEmote; 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; @@ -55,6 +57,20 @@ public class BotService implements Bot { } } + @Override + public void deleteMessage(Long serverId, Long channelId, Long messageId) { + getTextChannelFromServer(serverId, channelId).deleteMessageById(messageId).queue(); + } + + @Override + public Emote getEmote(Long serverId, AEmote emote) { + if(!emote.getCustom()) { + return null; + } + Guild guildById = getGuildById(serverId); + return guildById.getEmoteById(emote.getEmoteId()); + } + @Override public TextChannel getTextChannelFromServer(Long serverId, Long textChannelId) { Guild guild = getGuildById(serverId); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ConfigServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ConfigServiceBean.java new file mode 100644 index 000000000..0b22d6c26 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/ConfigServiceBean.java @@ -0,0 +1,27 @@ +package dev.sheldan.abstracto.core.service; + +import dev.sheldan.abstracto.core.management.ConfigManagementService; +import dev.sheldan.abstracto.core.models.database.AConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ConfigServiceBean implements ConfigService{ + + @Autowired + private ConfigManagementService configManagementService; + + @Override + public Double getDoubleValue(String name, Long serverId) { + return getDoubleValue(name, serverId, 0D); + } + + @Override + public Double getDoubleValue(String name, Long serverId, Double defaultValue) { + AConfig config = configManagementService.loadConfig(serverId, name); + if(config == null) { + return defaultValue; + } + return config.getDoubleValue(); + } +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/EmoteServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/EmoteServiceBean.java index 27cfb6730..57803ef56 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/EmoteServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/EmoteServiceBean.java @@ -1,11 +1,15 @@ package dev.sheldan.abstracto.core.service; +import dev.sheldan.abstracto.core.models.database.AEmote; +import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Emote; import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.MessageReaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Slf4j public class EmoteServiceBean implements EmoteService { @Autowired @@ -21,4 +25,36 @@ public class EmoteServiceBean implements EmoteService { } return false; } + + @Override + public AEmote buildAEmoteFromReaction(MessageReaction.ReactionEmote reaction) { + if(reaction.isEmote()) { + return AEmote.builder().emoteKey(reaction.getName()).custom(true).emoteId(reaction.getEmote().getIdLong()).animated(reaction.getEmote().isAnimated()).build(); + } else { + return AEmote.builder().emoteKey(reaction.getEmoji()).custom(false).build(); + } + } + + @Override + public String getEmoteAsMention(AEmote emote, Long serverId, String defaultText) { + if(emote != null && emote.getCustom()) { + Emote emote1 = botService.getEmote(serverId, emote); + if (emote1 != null) { + return emote1.getAsMention(); + } else { + log.warn("Emote {} with name {} in server {} defined, but not usable.", emote.getEmoteId(), emote.getName(), serverId); + return defaultText; + } + } else { + if(emote == null) { + return defaultText; + } + return emote.getEmoteKey(); + } + } + + @Override + public String getEmoteAsMention(AEmote emote, Long serverId) { + return this.getEmoteAsMention(emote, serverId, " "); + } } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageCacheBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageCacheBean.java index 0f91ebd87..6267c2033 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageCacheBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageCacheBean.java @@ -1,15 +1,22 @@ package dev.sheldan.abstracto.core.service; +import dev.sheldan.abstracto.core.management.EmoteManagementService; +import dev.sheldan.abstracto.core.management.UserManagementService; import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.CachedReaction; +import dev.sheldan.abstracto.core.models.database.AUser; import dev.sheldan.abstracto.core.models.embed.*; import lombok.extern.slf4j.Slf4j; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.MessageReaction; import net.dv8tion.jda.api.entities.TextChannel; +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.scheduling.annotation.Async; import org.springframework.stereotype.Component; import java.awt.*; @@ -18,54 +25,71 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -@Component @Slf4j -@CacheConfig(cacheNames = {"messages"}) +@Component +@Slf4j public class MessageCacheBean implements MessageCache { @Autowired private Bot bot; + @Autowired + private UserManagementService userManagementService; + + @Autowired + private EmoteManagementService emoteManagementService; + + @Autowired + private EmoteService emoteService; + + @Autowired + @Lazy + private MessageCache self; + @Override - @CachePut(key = "#message.id") - public CachedMessage putMessageInCache(Message message) { - log.debug("Adding message {} to cache", message.getId()); - return buildCachedMessageFromMessage(message); + @CachePut(key = "#message.id", cacheNames = "messages") + public CompletableFuture putMessageInCache(Message message) { + log.info("Adding message {} to cache", message.getId()); + CompletableFuture future = new CompletableFuture<>(); + self.buildCachedMessageFromMessage(future, message); + return future; } + @CachePut(key = "#message.messageId") - public CachedMessage putMessageInCache(CachedMessage message) { - return message; + public CompletableFuture putMessageInCache(CachedMessage message) { + log.info("Adding cached message to cache"); + return CompletableFuture.completedFuture(message); } @Override - public CachedMessage getMessageFromCache(Message message) throws ExecutionException, InterruptedException { - log.debug("Retrieving message {}", message.getId()); + @Cacheable(key = "#message.id", cacheNames = "messages") + public CompletableFuture getMessageFromCache(Message message) { + log.info("Retrieving message {}", message.getId()); return getMessageFromCache(message.getGuild().getIdLong(), message.getChannel().getIdLong(), message.getIdLong()); } @Override - @Cacheable(key = "#messageId.toString()") - public CachedMessage getMessageFromCache(Long guildId, Long textChannelId, Long messageId) throws ExecutionException, InterruptedException { + @Cacheable(key = "#messageId.toString()", cacheNames = "messages") + public CompletableFuture getMessageFromCache(Long guildId, Long textChannelId, Long messageId) { log.info("Retrieving message with parameters"); - CompletableFuture cachedMessageCompletableFuture = - getMessage(guildId, textChannelId, messageId) - .thenApply(jdaMessage -> { - CachedMessage cachedMessage = buildCachedMessageFromMessage(jdaMessage); - putMessageInCache(cachedMessage); - return cachedMessage; - }); + CompletableFuture cachedMessageCompletableFuture = new CompletableFuture<>(); + self.loadMessage(cachedMessageCompletableFuture, guildId, textChannelId, messageId); + return cachedMessageCompletableFuture; + } - return cachedMessageCompletableFuture.get(); + @Async + @Override + public void loadMessage(CompletableFuture future, Long guildId, Long textChannelId, Long messageId) { + TextChannel textChannelById = bot.getTextChannelFromServer(guildId, textChannelId); + textChannelById.retrieveMessageById(messageId).queue(message -> { + buildCachedMessageFromMessage(future, message); + }); } @Override - public CompletableFuture getMessage(Long guildId, Long textChannelId, Long messageId) { - TextChannel textChannelById = bot.getTextChannelFromServer(guildId, textChannelId); - return textChannelById.retrieveMessageById(messageId).submit(); - } - - private CachedMessage buildCachedMessageFromMessage(Message message) { + @Async + public void buildCachedMessageFromMessage(CompletableFuture future, Message message) { List attachmentUrls = new ArrayList<>(); message.getAttachments().forEach(attachment -> { attachmentUrls.add(attachment.getProxyUrl()); @@ -74,16 +98,55 @@ public class MessageCacheBean implements MessageCache { message.getEmbeds().forEach(embed -> { embeds.add(getCachedEmbedFromEmbed(embed)); }); - return CachedMessage.builder() - .authorId(message.getAuthor().getIdLong()) - .serverId(message.getGuild().getIdLong()) - .messageId(message.getIdLong()) - .channelId(message.getChannel().getIdLong()) - .content(message.getContentRaw()) - .embeds(embeds) - .timeCreated(message.getTimeCreated()) - .attachmentUrls(attachmentUrls) - .build(); + + List> futures = new ArrayList<>(); + message.getReactions().forEach(messageReaction -> { + CompletableFuture future1 = new CompletableFuture<>(); + self.getCachedReactionFromReaction(future1, messageReaction); + futures.add(future1); + }); + + 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(message.getTimeCreated()) + .attachmentUrls(attachmentUrls) + .build()); + }); + } + + private List getFutures(List> futures) { + List 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); + } + }); + return reactions; + } + + @Override + @Async + public void getCachedReactionFromReaction(CompletableFuture future, MessageReaction reaction) { + ReactionPaginationAction users = reaction.retrieveUsers().cache(false); + CachedReaction.CachedReactionBuilder builder = CachedReaction.builder(); + + List ausers = new ArrayList<>(); + users.forEachAsync(user -> { + ausers.add(AUser.builder().id(user.getIdLong()).build()); + return false; + }).thenAccept(o -> future.complete(builder.build())); + builder.users(ausers); + builder.emote(emoteService.buildAEmoteFromReaction(reaction.getReactionEmote())); } private CachedEmbed getCachedEmbedFromEmbed(MessageEmbed embed) { diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java index 66dda695f..86c011c41 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/MessageServiceBean.java @@ -9,6 +9,8 @@ import net.dv8tion.jda.api.entities.Message; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.util.Optional; + @Component @Slf4j public class MessageServiceBean implements MessageService { @@ -22,16 +24,21 @@ public class MessageServiceBean implements MessageService { @Override public void addReactionToMessage(String emoteKey, Long serverId, Message message) { Guild guildById = bot.getGuildById(serverId); - AEmote emote = emoteManagementService.loadEmoteByName(emoteKey, serverId); - if(emote.getCustom()) { - Emote emoteById = guildById.getEmoteById(emote.getEmoteId()); - if(emoteById != null) { - message.addReaction(emoteById).queue(); + Optional aEmote = emoteManagementService.loadEmoteByName(emoteKey, serverId); + if(aEmote.isPresent()) { + AEmote emote = aEmote.get(); + if(emote.getCustom()) { + Emote emoteById = guildById.getEmoteById(emote.getEmoteId()); + if(emoteById != null) { + message.addReaction(emoteById).queue(); + } else { + log.warn("Emote with key {} and id {} for guild {} was not found.", emoteKey, emote.getEmoteId(), guildById.getId()); + } } else { - log.warn("Emote with key {} and id {} for guild {} was not found.", emoteKey, emote.getEmoteId(), guildById.getId()); + message.addReaction(emote.getEmoteKey()).queue(); } } else { - message.addReaction(emote.getEmoteKey()).queue(); + log.warn("Cannot add reaction, emote {} not defined for server {}.", emoteKey, serverId); } } } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PostTargetServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PostTargetServiceBean.java index c48ac7557..b5762e1d8 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PostTargetServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/PostTargetServiceBean.java @@ -11,6 +11,7 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.TextChannel; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -93,7 +94,60 @@ public class PostTargetServiceBean implements PostTargetService { @Override public CompletableFuture sendEmbedInPostTarget(MessageToSend message, PostTarget target) { TextChannel textChannelForPostTarget = getTextChannelForPostTarget(target); - return textChannelForPostTarget.sendMessage(message.getMessage()).embed(message.getEmbed()).submit(); + String messageText = message.getMessage(); + if(StringUtils.isBlank(messageText)) { + return textChannelForPostTarget.sendMessage(message.getEmbed()).submit(); + } else { + return textChannelForPostTarget.sendMessage(messageText).embed(message.getEmbed()).submit(); + } + } + + @Override + public CompletableFuture editEmbedInPostTarget(Long messageId, MessageToSend message, PostTarget target) { + TextChannel textChannelForPostTarget = getTextChannelForPostTarget(target); + String messageText = message.getMessage(); + if(StringUtils.isBlank(messageText)) { + return textChannelForPostTarget.editMessageById(messageId, message.getEmbed()).submit(); + } else { + return textChannelForPostTarget.editMessageById(messageId, messageText).embed(message.getEmbed()).submit(); + } + } + + @Override + public void editOrCreatedInPostTarget(Long messageId, MessageToSend messageToSend, PostTarget target, CompletableFuture future) { + TextChannel textChannelForPostTarget = getTextChannelForPostTarget(target); + if(StringUtils.isBlank(messageToSend.getMessage().trim())) { + textChannelForPostTarget + .retrieveMessageById(messageId) + .queue( + existingMessage -> existingMessage + .editMessage(messageToSend.getEmbed()) + .submit().thenAccept(future::complete), + throwable -> sendEmbedInPostTarget(messageToSend, target) + .thenAccept(future::complete)); + } else { + textChannelForPostTarget + .retrieveMessageById(messageId) + .queue( + existingMessage -> existingMessage + .editMessage(messageToSend.getMessage()) + .embed(messageToSend.getEmbed()) + .submit().thenAccept(future::complete), + throwable -> sendEmbedInPostTarget(messageToSend, target) + .thenAccept(future::complete)); + } + } + + @Override + public void editOrCreatedInPostTarget(Long messageId, MessageToSend messageToSend, String postTargetName, Long serverId, CompletableFuture future) { + PostTarget postTarget = this.getPostTarget(postTargetName, serverId); + this.editOrCreatedInPostTarget(messageId, messageToSend, postTarget, future); + } + + @Override + public CompletableFuture editEmbedInPostTarget(Long messageId, MessageToSend message, String postTargetName, Long serverId) { + PostTarget postTarget = this.getPostTarget(postTargetName, serverId); + return editEmbedInPostTarget(messageId, message, postTarget); } @Override diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupManager.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupManager.java index c07e44389..020b9b00e 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupManager.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/StartupManager.java @@ -1,6 +1,7 @@ package dev.sheldan.abstracto.core.service; -import dev.sheldan.abstracto.SnowflakeUtils; +import dev.sheldan.abstracto.core.listener.ServerConfigListener; +import dev.sheldan.abstracto.core.utils.SnowflakeUtils; import dev.sheldan.abstracto.core.management.ChannelManagementService; import dev.sheldan.abstracto.core.management.RoleManagementService; import dev.sheldan.abstracto.core.management.ServerManagementService; @@ -42,6 +43,9 @@ public class StartupManager implements Startup { @Autowired private RoleManagementService roleManagementService; + @Autowired + private List configListeners; + @Override public void startBot() throws LoginException { @@ -68,6 +72,9 @@ public class StartupManager implements Startup { if(newGuild != null){ synchronizeRolesOf(newGuild, newAServer); synchronizeChannelsOf(newGuild, newAServer); + configListeners.forEach(serverConfigListener -> { + serverConfigListener.updateServerConfig(newAServer); + }); } }); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementServiceBean.java new file mode 100644 index 000000000..c4e8519ea --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ConfigManagementServiceBean.java @@ -0,0 +1,91 @@ +package dev.sheldan.abstracto.core.service.management; + +import dev.sheldan.abstracto.core.management.ServerManagementService; +import dev.sheldan.abstracto.core.models.database.AConfig; +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.management.ConfigManagementService; +import dev.sheldan.abstracto.repository.ConfigRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class ConfigManagementServiceBean implements ConfigManagementService { + + @Autowired + private ConfigRepository configRepository; + + @Autowired + private ServerManagementService serverManagementService; + + @Override + public void setOrCreateStringValue(Long serverId, String name, String value) { + AConfig config = loadConfig(serverId, name); + if(config == null) { + createConfig(serverId, name, value); + } else { + config.setStringValue(value); + configRepository.save(config); + } + } + + @Override + public void setOrCreateDoubleValue(Long serverId, String name, Double value) { + AConfig config = loadConfig(serverId, name); + if(config == null) { + createConfig(serverId, name, value); + } else { + config.setDoubleValue(value); + configRepository.save(config); + } + } + + @Override + public AConfig createConfig(Long serverId, String name, String value) { + AServer server = serverManagementService.loadServer(serverId); + AConfig config = AConfig + .builder() + .stringValue(value) + .server(server) + .name(name) + .build(); + configRepository.save(config); + return config; + } + + @Override + public AConfig createConfig(Long serverId, String name, Double value) { + AServer server = serverManagementService.loadServer(serverId); + AConfig config = AConfig + .builder() + .doubleValue(value) + .server(server) + .name(name) + .build(); + configRepository.save(config); + return config; + } + + @Override + public AConfig createIfNotExists(Long serverId, String name, String value) { + AConfig config = loadConfig(serverId, name); + if(config == null) { + return this.createConfig(serverId, name, value); + } + return config; + } + + @Override + public AConfig createIfNotExists(Long serverId, String name, Double value) { + AConfig config = loadConfig(serverId, name); + if(config == null) { + return this.createConfig(serverId, name, value); + } + return config; + } + + @Override + public AConfig loadConfig(Long serverId, String name) { + return configRepository.findAConfigByServerIdAndName(serverId, name); + } + +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/EmoteManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/EmoteManagementServiceBean.java index 5ed89b61f..332ac5e2c 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/EmoteManagementServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/EmoteManagementServiceBean.java @@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; +import java.util.Optional; @Component public class EmoteManagementServiceBean implements EmoteManagementService { @@ -73,24 +74,25 @@ public class EmoteManagementServiceBean implements EmoteManagementService { } @Override - public AEmote loadEmoteByName(String name, Long serverId) { + public Optional loadEmoteByName(String name, Long serverId) { AServer server = serverManagementService.loadServer(serverId); return loadEmoteByName(name, server); } @Override - public AEmote loadEmoteByName(String name, AServer server) { - return repository.findAEmoteByNameAndServerRef(name, server); + public Optional loadEmoteByName(String name, AServer server) { + return Optional.ofNullable(repository.findAEmoteByNameAndServerRef(name, server)); } @Override public AEmote setEmoteToCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId) { AServer server = serverManagementService.loadServer(serverId); AEmote emote; - if(!emoteExists(name, server)) { + Optional emoteOptional = loadEmoteByName(name, server); + if(!emoteOptional.isPresent()) { emote = this.createCustomEmote(name, emoteKey, emoteId, animated, server); } else { - emote = loadEmoteByName(name, server); + emote = emoteOptional.get(); emote.setEmoteKey(emoteKey); emote.setEmoteId(emoteId); emote.setAnimated(animated); @@ -104,11 +106,14 @@ public class EmoteManagementServiceBean implements EmoteManagementService { public AEmote setEmoteToCustomEmote(String name, Emote emote, Long serverId) { AServer server = serverManagementService.loadServer(serverId); AEmote emoteBeingSet; - if(!emoteExists(name, server)) { - emoteBeingSet = this.createDefaultEmote(name, emote.getName(), server); + Optional emoteOptional = loadEmoteByName(name, serverId); + if(!emoteOptional.isPresent()) { + emoteBeingSet = this.createCustomEmote(name, emote.getName(), emote.getIdLong(), emote.isAnimated(), server); } else { - emoteBeingSet = loadEmoteByName(name, serverId); - emoteBeingSet.setCustom(false); + emoteBeingSet = emoteOptional.get(); + emoteBeingSet.setCustom(true); + emoteBeingSet.setEmoteId(emote.getIdLong()); + emoteBeingSet.setAnimated(emote.isAnimated()); emoteBeingSet.setEmoteKey(emote.getName()); repository.save(emoteBeingSet); } @@ -117,11 +122,18 @@ public class EmoteManagementServiceBean implements EmoteManagementService { @Override public AEmote setEmoteToDefaultEmote(String name, String emoteKey, Long serverId) { - AEmote existing = loadEmoteByName(name, serverId); - existing.setEmoteKey(emoteKey); - existing.setCustom(false); - repository.save(existing); - return existing; + AServer server = serverManagementService.loadServer(serverId); + AEmote emoteBeingSet; + Optional emoteOptional = loadEmoteByName(name, serverId); + if(!emoteOptional.isPresent()) { + emoteBeingSet = this.createDefaultEmote(name, emoteKey, server); + } else { + emoteBeingSet = emoteOptional.get(); + emoteBeingSet.setEmoteKey(emoteKey); + emoteBeingSet.setCustom(false); + repository.save(emoteBeingSet); + } + return emoteBeingSet; } @Override diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ServerManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ServerManagementServiceBean.java index d0f948825..874648c6b 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ServerManagementServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ServerManagementServiceBean.java @@ -10,6 +10,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.List; + @Service @Slf4j public class ServerManagementServiceBean implements ServerManagementService { @@ -43,6 +45,8 @@ public class ServerManagementServiceBean implements ServerManagementService { @Override public void addChannelToServer(AServer server, AChannel channel) { server.getChannels().add(channel); + channel.setServer(server); + repository.save(server); } @Override @@ -83,5 +87,10 @@ public class ServerManagementServiceBean implements ServerManagementService { return getPostTarget(server, target); } + @Override + public List getAllServers() { + return repository.findAll(); + } + } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/SnowflakeUtils.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/utils/SnowflakeUtils.java similarity index 93% rename from abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/SnowflakeUtils.java rename to abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/utils/SnowflakeUtils.java index 902ad4692..2f8700ee7 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/SnowflakeUtils.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/utils/SnowflakeUtils.java @@ -1,4 +1,4 @@ -package dev.sheldan.abstracto; +package dev.sheldan.abstracto.core.utils; import dev.sheldan.abstracto.core.models.SnowFlake; import net.dv8tion.jda.api.entities.ISnowflake; diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/listener/MessageEmbedListener.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/listener/MessageEmbedListener.java index 61a46dbdc..abaa02e0b 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/listener/MessageEmbedListener.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/listener/MessageEmbedListener.java @@ -73,13 +73,11 @@ public class MessageEmbedListener extends ListenerAdapter { Long serverIdLong = Long.parseLong(serverId); Long channelIdLong = Long.parseLong(channelId); Long messageIdLong = Long.parseLong(messageId); - try { - CachedMessage messageFromCache = messageCache.getMessageFromCache(serverIdLong, channelIdLong, messageIdLong); - messageRaw = messageRaw.replace(wholeLink, ""); - self.createEmbedAndPostEmbed(event, messageFromCache); - } catch (ExecutionException | InterruptedException e) { - log.warn("Failed to load message from cache", e); - } + messageRaw = messageRaw.replace(wholeLink, ""); + messageCache.getMessageFromCache(serverIdLong, channelIdLong, messageIdLong).thenAccept(cachedMessage -> { + self.createEmbedAndPostEmbed(event, cachedMessage); + }); + } } if(StringUtils.isBlank(messageRaw) && matched) { @@ -92,7 +90,11 @@ public class MessageEmbedListener extends ListenerAdapter { public void createEmbedAndPostEmbed(@Nonnull GuildMessageReceivedEvent event, CachedMessage message) { MessageEmbeddedModel messageEmbeddedModel = buildTemplateParameter(event, message); MessageToSend embed = templateService.renderEmbedTemplate(MESSAGE_EMBED_TEMPLATE, messageEmbeddedModel); - event.getChannel().sendMessage(embed.getMessage()).embed(embed.getEmbed()).queue(); + if(StringUtils.isBlank(embed.getMessage())) { + event.getChannel().sendMessage(embed.getEmbed()).queue(); + } else { + event.getChannel().sendMessage(embed.getMessage()).embed(embed.getEmbed()).queue(); + } } private MessageEmbeddedModel buildTemplateParameter(GuildMessageReceivedEvent event, CachedMessage embeddedMessage) { diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/listener/MessageUpdatedListener.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/listener/MessageUpdatedListener.java index 0a4182972..7a7d919cb 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/listener/MessageUpdatedListener.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/listener/MessageUpdatedListener.java @@ -1,6 +1,6 @@ package dev.sheldan.abstracto.listener; -import dev.sheldan.abstracto.core.MessageTextUpdatedListener; +import dev.sheldan.abstracto.core.listener.MessageTextUpdatedListener; import dev.sheldan.abstracto.core.models.CachedMessage; import dev.sheldan.abstracto.core.service.MessageCache; import lombok.extern.slf4j.Slf4j; @@ -9,10 +9,10 @@ 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.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; import javax.annotation.Nonnull; import java.util.List; -import java.util.concurrent.ExecutionException; @Component @Slf4j @@ -24,17 +24,23 @@ public class MessageUpdatedListener extends ListenerAdapter { @Autowired private MessageCache messageCache; + @Autowired + private MessageUpdatedListener self; + @Override public void onGuildMessageUpdate(@Nonnull GuildMessageUpdateEvent event) { Message message = event.getMessage(); - try { - CachedMessage fromCache = messageCache.getMessageFromCache(message.getGuild().getIdLong(), message.getTextChannel().getIdLong(), event.getMessageIdLong()); - listener.forEach(messageTextUpdatedListener -> { - messageTextUpdatedListener.execute(fromCache, message); - }); + messageCache.getMessageFromCache(message.getGuild().getIdLong(), message.getTextChannel().getIdLong(), event.getMessageIdLong()).thenAccept(cachedMessage -> { + self.executeListener(message, cachedMessage); messageCache.putMessageInCache(message); - } catch (ExecutionException | InterruptedException e) { - log.warn("Failed to load message", e); - } + }); + + } + + @Transactional + public void executeListener(Message message, CachedMessage cachedMessage) { + listener.forEach(messageTextUpdatedListener -> { + messageTextUpdatedListener.execute(cachedMessage, message); + }); } } diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/listener/ReactionUpdatedListener.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/listener/ReactionUpdatedListener.java new file mode 100644 index 000000000..2bc865bed --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/listener/ReactionUpdatedListener.java @@ -0,0 +1,137 @@ +package dev.sheldan.abstracto.listener; + +import dev.sheldan.abstracto.core.listener.ReactedAddedListener; +import dev.sheldan.abstracto.core.listener.ReactedRemovedListener; +import dev.sheldan.abstracto.core.management.UserManagementService; +import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.CachedReaction; +import dev.sheldan.abstracto.core.models.database.AUser; +import dev.sheldan.abstracto.core.models.database.AUserInAServer; +import dev.sheldan.abstracto.core.service.EmoteService; +import dev.sheldan.abstracto.core.service.MessageCache; +import dev.sheldan.abstracto.core.utils.EmoteUtils; +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.Transactional; + +import javax.annotation.Nonnull; +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 EmoteService emoteService; + + @Autowired + private UserManagementService userManagementService; + + @Autowired + private List addedListenerList; + + @Autowired + private List reactionRemovedListener; + + @Autowired + private ReactionUpdatedListener self; + + + @Override + @Transactional + public void onGuildMessageReactionAdd(@Nonnull GuildMessageReactionAddEvent event) { + CompletableFuture asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong()); + asyncMessageFromCache.thenAccept(cachedMessage -> { + CompletableFuture future = new CompletableFuture<>(); + messageCache.getCachedReactionFromReaction(future, event.getReaction()); + future.thenAccept(reaction -> { + self.callAddedListeners(event, cachedMessage, reaction); + messageCache.putMessageInCache(cachedMessage); + }); + }); + } + + private void addReactionIfNotThere(CachedMessage message, CachedReaction reaction, AUser userReacting) { + Optional existingReaction = message.getReactions().stream().filter(reaction1 -> { + return EmoteUtils.compareAEmote(reaction1.getEmote(), reaction.getEmote()); + }).findAny(); + if(!existingReaction.isPresent()) { + message.getReactions().add(reaction); + } else { + CachedReaction cachedReaction = existingReaction.get(); + Optional any = cachedReaction.getUsers().stream().filter(user -> user.getId().equals(userReacting.getId())).findAny(); + if(!any.isPresent()){ + cachedReaction.getUsers().add(userReacting); + } + } + } + + private void removeReactionIfThere(CachedMessage message, CachedReaction reaction, AUser userReacting) { + Optional existingReaction = message.getReactions().stream().filter(reaction1 -> { + return EmoteUtils.compareAEmote(reaction1.getEmote(), reaction.getEmote()); + }).findAny(); + if(existingReaction.isPresent()) { + CachedReaction cachedReaction = existingReaction.get(); + cachedReaction.getUsers().removeIf(user -> user.getId().equals(userReacting.getId())); + message.getReactions().removeIf(reaction1 -> reaction1.getUsers().isEmpty()); + } + } + + @Transactional + public void callAddedListeners(@Nonnull GuildMessageReactionAddEvent event, CachedMessage cachedMessage, CachedReaction reaction) { + AUserInAServer userInAServer = userManagementService.loadUser(event.getGuild().getIdLong(), event.getUserIdLong()); + addReactionIfNotThere(cachedMessage, reaction, userInAServer.getUserReference()); + try { + addedListenerList.forEach(reactedAddedListener -> { + reactedAddedListener.executeReactionAdded(cachedMessage, event.getReaction(), userInAServer); + }); + } catch (Exception e) { + log.warn("Exception on reaction added handler:", e); + } + } + + @Override + @Transactional + public void onGuildMessageReactionRemove(@Nonnull GuildMessageReactionRemoveEvent event) { + CompletableFuture asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong()); + asyncMessageFromCache.thenAccept(cachedMessage -> { + CompletableFuture future = new CompletableFuture<>(); + messageCache.getCachedReactionFromReaction(future, event.getReaction()); + future.thenAccept(reaction -> { + self.callRemoveListeners(event, cachedMessage, reaction); + }); + + messageCache.putMessageInCache(cachedMessage); + }); + } + + @Transactional + public void callRemoveListeners(@Nonnull GuildMessageReactionRemoveEvent event, CachedMessage cachedMessage, CachedReaction reaction) { + AUserInAServer userInAServer = userManagementService.loadUser(event.getGuild().getIdLong(), event.getUserIdLong()); + removeReactionIfThere(cachedMessage, reaction, userInAServer.getUserReference()); + reactionRemovedListener.forEach(reactedAddedListener -> { + reactedAddedListener.executeReactionRemoved(cachedMessage, event.getReaction(), userInAServer); + }); + } + + @Override + @Transactional + public void onGuildMessageReactionRemoveAll(@Nonnull GuildMessageReactionRemoveAllEvent event) { + CompletableFuture asyncMessageFromCache = messageCache.getMessageFromCache(event.getGuild().getIdLong(), event.getChannel().getIdLong(), event.getMessageIdLong()); + asyncMessageFromCache.thenAccept(cachedMessage -> { + cachedMessage.getReactions().clear(); + messageCache.putMessageInCache(cachedMessage); + }); + } + +} diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/repository/ConfigRepository.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/repository/ConfigRepository.java new file mode 100644 index 000000000..b68571e68 --- /dev/null +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/repository/ConfigRepository.java @@ -0,0 +1,8 @@ +package dev.sheldan.abstracto.repository; + +import dev.sheldan.abstracto.core.models.database.AConfig; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ConfigRepository extends JpaRepository { + AConfig findAConfigByServerIdAndName(Long serverId, String name); +} diff --git a/abstracto-application/core/core-impl/src/main/resources/seedData/templates/coreEvents/message_embed_en_US.ftl b/abstracto-application/core/core-impl/src/main/resources/seedData/templates/coreEvents/message_embed_en_US.ftl index 0cf2b2aee..92d83becf 100644 --- a/abstracto-application/core/core-impl/src/main/resources/seedData/templates/coreEvents/message_embed_en_US.ftl +++ b/abstracto-application/core/core-impl/src/main/resources/seedData/templates/coreEvents/message_embed_en_US.ftl @@ -1,7 +1,7 @@ { "author": { "name": "${author.effectiveName}", - "avatar": "${author.user.avatarUrl}" + "avatar": "${author.user.effectiveAvatarUrl}" }, "color" : { "r": 200, diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/MessageTextUpdatedListener.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/MessageTextUpdatedListener.java similarity index 83% rename from abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/MessageTextUpdatedListener.java rename to abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/MessageTextUpdatedListener.java index c0e409975..fb5665d71 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/MessageTextUpdatedListener.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/MessageTextUpdatedListener.java @@ -1,4 +1,4 @@ -package dev.sheldan.abstracto.core; +package dev.sheldan.abstracto.core.listener; import dev.sheldan.abstracto.core.models.CachedMessage; import net.dv8tion.jda.api.entities.Message; diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/ReactedAddedListener.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/ReactedAddedListener.java new file mode 100644 index 000000000..1d95946ae --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/ReactedAddedListener.java @@ -0,0 +1,9 @@ +package dev.sheldan.abstracto.core.listener; + +import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.database.AUserInAServer; +import net.dv8tion.jda.api.entities.MessageReaction; + +public interface ReactedAddedListener { + void executeReactionAdded(CachedMessage message, MessageReaction reaction, AUserInAServer userAdding); +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/ReactedRemovedListener.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/ReactedRemovedListener.java new file mode 100644 index 000000000..d4bbe7247 --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/ReactedRemovedListener.java @@ -0,0 +1,9 @@ +package dev.sheldan.abstracto.core.listener; + +import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.database.AUserInAServer; +import net.dv8tion.jda.api.entities.MessageReaction; + +public interface ReactedRemovedListener { + void executeReactionRemoved(CachedMessage message, MessageReaction reaction, AUserInAServer userRemoving); +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/ServerConfigListener.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/ServerConfigListener.java new file mode 100644 index 000000000..fbca2165c --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/listener/ServerConfigListener.java @@ -0,0 +1,7 @@ +package dev.sheldan.abstracto.core.listener; + +import dev.sheldan.abstracto.core.models.database.AServer; + +public interface ServerConfigListener { + void updateServerConfig(AServer server); +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/management/ConfigManagementService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/management/ConfigManagementService.java new file mode 100644 index 000000000..d21b9c806 --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/management/ConfigManagementService.java @@ -0,0 +1,13 @@ +package dev.sheldan.abstracto.core.management; + +import dev.sheldan.abstracto.core.models.database.AConfig; + +public interface ConfigManagementService { + void setOrCreateStringValue(Long serverId, String name, String value); + void setOrCreateDoubleValue(Long serverId, String name, Double value); + AConfig createConfig(Long serverId, String name, String value); + AConfig createConfig(Long serverId, String name, Double value); + AConfig createIfNotExists(Long serverId, String name, String value); + AConfig createIfNotExists(Long serverId, String name, Double value); + AConfig loadConfig(Long serverId, String name); +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/management/EmoteManagementService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/management/EmoteManagementService.java index bd5f23189..72fd22915 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/management/EmoteManagementService.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/management/EmoteManagementService.java @@ -4,14 +4,16 @@ import dev.sheldan.abstracto.core.models.database.AEmote; import dev.sheldan.abstracto.core.models.database.AServer; import net.dv8tion.jda.api.entities.Emote; +import java.util.Optional; + public interface EmoteManagementService { AEmote loadEmote(Long id); AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId); AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, AServer server); AEmote createDefaultEmote(String name, String emoteKey, Long serverId); AEmote createDefaultEmote(String name, String emoteKey, AServer server); - AEmote loadEmoteByName(String name, Long serverId); - AEmote loadEmoteByName(String name, AServer server); + Optional loadEmoteByName(String name, Long serverId); + Optional loadEmoteByName(String name, AServer server); AEmote setEmoteToCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId); AEmote setEmoteToCustomEmote(String name, Emote emote, Long serverId); AEmote setEmoteToDefaultEmote(String name, String emoteKey, Long serverId); diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/management/ServerManagementService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/management/ServerManagementService.java index 8aab6032b..d7616bbab 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/management/ServerManagementService.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/management/ServerManagementService.java @@ -2,6 +2,8 @@ package dev.sheldan.abstracto.core.management; import dev.sheldan.abstracto.core.models.database.*; +import java.util.List; + public interface ServerManagementService { AServer createServer(Long id); AServer loadServer(Long id); @@ -12,4 +14,5 @@ public interface ServerManagementService { AChannel getPostTarget(Long serverId, PostTarget target); AChannel getPostTarget(AServer server, PostTarget target); AChannel getPostTarget(AServer server, String name); + List getAllServers(); } diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/AServerChannelMessage.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/AServerChannelMessage.java new file mode 100644 index 000000000..02d9937b1 --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/AServerChannelMessage.java @@ -0,0 +1,16 @@ +package dev.sheldan.abstracto.core.models; + +import dev.sheldan.abstracto.core.models.database.AChannel; +import dev.sheldan.abstracto.core.models.database.AServer; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +public class AServerChannelMessage { + private AServer server; + private AChannel channel; + private Long messageId; +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/CachedMessage.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/CachedMessage.java index f4c18589a..32216e792 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/CachedMessage.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/CachedMessage.java @@ -6,6 +6,7 @@ import lombok.Builder; import lombok.Getter; import lombok.Setter; +import java.time.Instant; import java.time.OffsetDateTime; import java.util.List; @@ -21,6 +22,7 @@ public class CachedMessage { private String content; private List embeds; private List attachmentUrls; + private List reactions; public String getMessageUrl() { return MessageUtils.buildMessageUrl(this.serverId ,this.channelId, this.messageId); diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/CachedReaction.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/CachedReaction.java new file mode 100644 index 000000000..ba860d450 --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/CachedReaction.java @@ -0,0 +1,17 @@ +package dev.sheldan.abstracto.core.models; + +import dev.sheldan.abstracto.core.models.database.AEmote; +import dev.sheldan.abstracto.core.models.database.AUser; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@Builder +public class CachedReaction { + private AEmote emote; + private List users; +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/ServerChannelMessage.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/ServerChannelMessage.java new file mode 100644 index 000000000..f9e1c08d1 --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/ServerChannelMessage.java @@ -0,0 +1,14 @@ +package dev.sheldan.abstracto.core.models; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +public class ServerChannelMessage { + private Long serverId; + private Long channelId; + private Long messageId; +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java index 6da40242a..b30042bf1 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AChannel.java @@ -17,6 +17,7 @@ public class AChannel implements SnowFlake { @Id @Getter + @Column(name = "id") public Long id; @Getter @@ -24,7 +25,9 @@ public class AChannel implements SnowFlake { private Set groups; @ManyToOne(fetch = FetchType.LAZY) - @Getter @Setter + @Getter + @Setter + @JoinColumn(name = "server_id") private AServer server; @Getter diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AConfig.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AConfig.java new file mode 100644 index 000000000..f4d594937 --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AConfig.java @@ -0,0 +1,35 @@ +package dev.sheldan.abstracto.core.models.database; + +import lombok.*; + +import javax.persistence.*; + +@Entity +@Table(name="systemConfig") +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +public class AConfig { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer Id; + + @Column + private String name; + + @Column + @Setter + private String stringValue; + + @Column + @Setter + private Double doubleValue; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "server_id", nullable = false) + @Getter + @Setter + private AServer server; +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java index 22f809ad5..636a9b8f3 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/models/database/AServer.java @@ -16,6 +16,7 @@ import java.util.List; public class AServer implements SnowFlake { @Id + @Column(name = "id") private Long id; private String name; @@ -26,10 +27,10 @@ public class AServer implements SnowFlake { @OneToMany( fetch = FetchType.LAZY, - mappedBy = "server", cascade = CascadeType.ALL, orphanRemoval = true) @Builder.Default + @JoinColumn(name = "server_id") private List channels = new ArrayList<>(); @OneToMany( diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/Bot.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/Bot.java index b5b069b5e..bb1024a8c 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/Bot.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/Bot.java @@ -1,7 +1,9 @@ package dev.sheldan.abstracto.core.service; import dev.sheldan.abstracto.core.models.ServerChannelUser; +import dev.sheldan.abstracto.core.models.database.AEmote; import net.dv8tion.jda.api.JDA; +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; @@ -15,6 +17,8 @@ public interface Bot { JDA getInstance(); ServerChannelUser getServerChannelUser(Long serverId, Long channelId, Long userId); Member getMemberInServer(Long serverId, Long memberId); + void deleteMessage(Long serverId, Long channelId, Long messageId); + Emote getEmote(Long serverId, AEmote emote); TextChannel getTextChannelFromServer(Long serverId, Long textChannelId); Guild getGuildById(Long serverId); void shutdown(); diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/ConfigService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/ConfigService.java new file mode 100644 index 000000000..409b676ef --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/ConfigService.java @@ -0,0 +1,7 @@ +package dev.sheldan.abstracto.core.service; + +public interface ConfigService { + Double getDoubleValue(String name, Long serverId); + Double getDoubleValue(String name, Long serverId, Double defaultValue); + +} diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/EmoteService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/EmoteService.java index 11f61d692..a6ae13e58 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/EmoteService.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/EmoteService.java @@ -1,7 +1,12 @@ package dev.sheldan.abstracto.core.service; +import dev.sheldan.abstracto.core.models.database.AEmote; import net.dv8tion.jda.api.entities.Emote; +import net.dv8tion.jda.api.entities.MessageReaction; public interface EmoteService { boolean isEmoteUsableByBot(Emote emote); + AEmote buildAEmoteFromReaction(MessageReaction.ReactionEmote reaction); + String getEmoteAsMention(AEmote emote, Long serverId, String defaultText); + String getEmoteAsMention(AEmote emote, Long serverId); } diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/MessageCache.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/MessageCache.java index 9d6c62e26..b36c85ea0 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/MessageCache.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/MessageCache.java @@ -1,14 +1,19 @@ package dev.sheldan.abstracto.core.service; import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.CachedReaction; import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageReaction; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public interface MessageCache { - CachedMessage putMessageInCache(Message message); - CachedMessage getMessageFromCache(Message message) throws ExecutionException, InterruptedException; - CachedMessage getMessageFromCache(Long guildId, Long textChannelId, Long messageId) throws ExecutionException, InterruptedException; - CompletableFuture getMessage(Long serverId, Long textChannelId, Long messageId); + CompletableFuture putMessageInCache(Message message); + CompletableFuture getMessageFromCache(Long guildId, Long textChannelId, Long messageId); + CompletableFuture getMessageFromCache(Message message); + CompletableFuture putMessageInCache(CachedMessage message); + void loadMessage(CompletableFuture future, Long guildId, Long textChannelId, Long messageId); + void getCachedReactionFromReaction(CompletableFuture future, MessageReaction reaction); + void buildCachedMessageFromMessage(CompletableFuture future, Message message); } diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/PostTargetService.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/PostTargetService.java index 24a1e5481..d0a4ee9db 100644 --- a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/PostTargetService.java +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/service/PostTargetService.java @@ -15,6 +15,10 @@ public interface PostTargetService { CompletableFuture sendEmbedInPostTarget(MessageEmbed embed, String postTargetName, Long serverId); CompletableFuture sendEmbedInPostTarget(MessageToSend message, String postTargetName, Long serverId); CompletableFuture sendEmbedInPostTarget(MessageToSend message, PostTarget target); + CompletableFuture editEmbedInPostTarget(Long messageId, MessageToSend message, PostTarget target); + CompletableFuture editEmbedInPostTarget(Long messageId, MessageToSend message, String postTargetName, Long serverId); + void editOrCreatedInPostTarget(Long messageId, MessageToSend messageToSend, PostTarget target, CompletableFuture future); + void editOrCreatedInPostTarget(Long messageId, MessageToSend messageToSend, String postTarget, Long serverId, CompletableFuture future); boolean validPostTarget(String name); List getAvailablePostTargets(); } diff --git a/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/utils/EmoteUtils.java b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/utils/EmoteUtils.java new file mode 100644 index 000000000..e9dd34ecb --- /dev/null +++ b/abstracto-application/core/core-interface/src/main/java/dev/sheldan/abstracto/core/utils/EmoteUtils.java @@ -0,0 +1,42 @@ +package dev.sheldan.abstracto.core.utils; + +import dev.sheldan.abstracto.core.models.CachedMessage; +import dev.sheldan.abstracto.core.models.CachedReaction; +import dev.sheldan.abstracto.core.models.database.AEmote; +import net.dv8tion.jda.api.entities.Emote; +import net.dv8tion.jda.api.entities.MessageReaction; + +import java.util.Optional; + +public class EmoteUtils { + + public static boolean isReactionEmoteAEmote(MessageReaction.ReactionEmote reaction, AEmote emote, Optional emoteInGuildOptional) { + if(reaction.isEmote() && emote.getCustom()) { + if(emoteInGuildOptional.isPresent()) { + Emote emoteInGuild = emoteInGuildOptional.get(); + return emoteInGuild.equals(reaction.getEmote()); + } else { + return false; + } + } else { + return reaction.getEmoji().equals(emote.getEmoteKey()); + } + } + + public static Optional getReactionFromMessageByEmote(CachedMessage message, AEmote emote) { + return message.getReactions().stream().filter(reaction -> compareAEmote(reaction.getEmote(), emote)).findFirst(); + } + + public static boolean compareAEmote(AEmote a, AEmote b) { + if(a.getCustom() && b.getCustom()) { + return a.getEmoteId().equals(b.getEmoteId()); + } else { + if(!a.getCustom() && !b.getCustom()) { + return a.getEmoteKey().equals(b.getEmoteKey()); + } else { + return false; + } + } + } + +} diff --git a/abstracto-application/executable/src/main/resources/application.properties b/abstracto-application/executable/src/main/resources/application.properties new file mode 100644 index 000000000..03a3a24c4 --- /dev/null +++ b/abstracto-application/executable/src/main/resources/application.properties @@ -0,0 +1,2 @@ +spring.quartz.job-store-type=jdbc +spring.quartz.jdbc.initialize-schema=never diff --git a/abstracto-application/scheduling/scheduling-impl/src/main/resources/config/application.properties b/abstracto-application/scheduling/scheduling-impl/src/main/resources/config/application.properties index 4850f6ae6..05af1e41a 100644 --- a/abstracto-application/scheduling/scheduling-impl/src/main/resources/config/application.properties +++ b/abstracto-application/scheduling/scheduling-impl/src/main/resources/config/application.properties @@ -1,6 +1,3 @@ -spring.quartz.job-store-type=jdbc -spring.quartz.jdbc.initialize-schema=never - spring.quartz.properties.org.quartz.scheduler.instanceName=quartz-abstracto-app spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO spring.quartz.properties.org.quartz.scheduler.instanceIdGenerator.class=dev.sheldan.abstracto.scheduling.service.IdGenerationService diff --git a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/config/FreemarkerConfiguration.java b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/config/FreemarkerConfiguration.java index 6e66339c1..07e5176d4 100644 --- a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/config/FreemarkerConfiguration.java +++ b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/config/FreemarkerConfiguration.java @@ -8,6 +8,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.ui.freemarker.FreeMarkerConfigurationFactory; import java.io.IOException; +import java.util.Locale; @org.springframework.context.annotation.Configuration public class FreemarkerConfiguration { @@ -20,6 +21,7 @@ public class FreemarkerConfiguration { FreeMarkerConfigurationFactory factory = new FreeMarkerConfigurationFactory(); factory.setPreTemplateLoaders(templateLoader); Configuration configuration = factory.createConfiguration(); + configuration.setEncoding(Locale.getDefault(), "utf-8"); // needed to support default methods in interfaces configuration.setIncompatibleImprovements(Configuration.VERSION_2_3_29); return configuration; diff --git a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/loading/TemplateServiceBean.java b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/loading/TemplateServiceBean.java index c21bd0a4d..47a599a9a 100644 --- a/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/loading/TemplateServiceBean.java +++ b/abstracto-application/templating/templating-impl/src/main/java/dev/sheldan/abstracto/templating/loading/TemplateServiceBean.java @@ -98,19 +98,6 @@ public class TemplateServiceBean implements TemplateService { .build(); } - private String impromptu(String templateStr, Object model) { - try { - Template t = new Template("name", new StringReader(templateStr), - new Configuration(Configuration.VERSION_2_3_29)); - return FreeMarkerTemplateUtils.processTemplateIntoString(t, model); - } catch (IOException e) { - e.printStackTrace(); - } catch (TemplateException e) { - e.printStackTrace(); - } - return ""; - } - @Override public String renderTemplate(String key, HashMap parameters) { try {