From 96d5ca06e7567f106d41d2976323183e2aab3f8a Mon Sep 17 00:00:00 2001 From: Sheldan <5037282+Sheldan@users.noreply.github.com> Date: Sat, 16 Aug 2025 13:07:56 +0200 Subject: [PATCH] [OPB-xxx] adding feature mode to fully automate news posts --- .../bot/modules/news/command/UpdateNews.java | 20 +++- .../bot/modules/news/config/NewsFeature.java | 6 ++ .../modules/news/config/NewsFeatureMode.java | 16 ++++ .../modules/news/model/ForumPostEntry.java | 24 +++++ .../modules/news/model/ForumPostModel.java | 12 +++ .../news/service/NewsSourceServiceBean.java | 91 +++++++++++++++---- .../news/src/main/resources/news.properties | 8 ++ .../default/job/newsForumPost_embed.ftl | 17 ++++ 8 files changed, 172 insertions(+), 22 deletions(-) create mode 100644 application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/config/NewsFeatureMode.java create mode 100644 application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/model/ForumPostEntry.java create mode 100644 application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/model/ForumPostModel.java create mode 100644 templates/oneplus-bot-templates/module-templates/news-templates/src/main/resources/default/job/newsForumPost_embed.ftl diff --git a/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/command/UpdateNews.java b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/command/UpdateNews.java index 89e2a8a..fa8d42b 100644 --- a/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/command/UpdateNews.java +++ b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/command/UpdateNews.java @@ -36,10 +36,24 @@ public class UpdateNews extends AbstractConditionableCommand { @Override public CommandConfiguration getConfiguration() { - Parameter newsPostId = Parameter.builder().name("newsPostId").type(Long.class).templated(true).build(); - Parameter newsText = Parameter.builder().name("text").type(String.class).remainder(true).templated(true).build(); + Parameter newsPostId = Parameter + .builder() + .name("newsPostId") + .type(Long.class) + .templated(true) + .build(); + Parameter newsText = Parameter + .builder() + .name("text") + .type(String.class) + .remainder(true) + .templated(true) + .build(); List parameters = Arrays.asList(newsPostId, newsText); - HelpInfo helpInfo = HelpInfo.builder().templated(true).build(); + HelpInfo helpInfo = HelpInfo + .builder() + .templated(true) + .build(); return CommandConfiguration.builder() .name("updateNews") .module(NewsModuleDefinition.NEWS) diff --git a/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/config/NewsFeature.java b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/config/NewsFeature.java index 8d76e84..4d9bbe2 100644 --- a/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/config/NewsFeature.java +++ b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/config/NewsFeature.java @@ -2,6 +2,7 @@ package dev.sheldan.oneplus.bot.modules.news.config; import dev.sheldan.abstracto.core.config.FeatureConfig; import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.config.FeatureMode; import dev.sheldan.abstracto.core.config.PostTargetEnum; import org.springframework.stereotype.Component; @@ -22,4 +23,9 @@ public class NewsFeature implements FeatureConfig { public List getRequiredPostTargets() { return Arrays.asList(NewsPostTarget.NEWS_TARGET, NewsPostTarget.FORUM_POST_NOTIFICATION); } + + @Override + public List getAvailableModes() { + return Arrays.asList(NewsFeatureMode.AUTOMATIC_POST, NewsFeatureMode.AUTOMATIC_PUBLISH); + } } diff --git a/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/config/NewsFeatureMode.java b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/config/NewsFeatureMode.java new file mode 100644 index 0000000..590d2f3 --- /dev/null +++ b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/config/NewsFeatureMode.java @@ -0,0 +1,16 @@ +package dev.sheldan.oneplus.bot.modules.news.config; + +import dev.sheldan.abstracto.core.config.FeatureMode; +import lombok.Getter; + +@Getter +public enum NewsFeatureMode implements FeatureMode { + AUTOMATIC_POST("automaticPost"), + AUTOMATIC_PUBLISH("automaticPublish"); + + private final String key; + + NewsFeatureMode(String key) { + this.key = key; + } +} diff --git a/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/model/ForumPostEntry.java b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/model/ForumPostEntry.java new file mode 100644 index 0000000..82e030d --- /dev/null +++ b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/model/ForumPostEntry.java @@ -0,0 +1,24 @@ +package dev.sheldan.oneplus.bot.modules.news.model; + +import dev.sheldan.oneplus.bot.modules.news.model.forum.ForumPost; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class ForumPostEntry { + private Long postId; + private String subject; + private String content; + private Long creatorId; + + public static ForumPostEntry fromPost(ForumPost forumPost) { + return ForumPostEntry + .builder() + .postId(forumPost.getId()) + .subject(forumPost.getSubject()) + .content(forumPost.getContent()) + .creatorId(forumPost.getSource().getUserId()) + .build(); + } +} diff --git a/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/model/ForumPostModel.java b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/model/ForumPostModel.java new file mode 100644 index 0000000..e788ad4 --- /dev/null +++ b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/model/ForumPostModel.java @@ -0,0 +1,12 @@ +package dev.sheldan.oneplus.bot.modules.news.model; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class ForumPostModel { + private List entries; +} diff --git a/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/service/NewsSourceServiceBean.java b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/service/NewsSourceServiceBean.java index 3d050d9..2edeae5 100644 --- a/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/service/NewsSourceServiceBean.java +++ b/application/oneplus-bot-modules/news/src/main/java/dev/sheldan/oneplus/bot/modules/news/service/NewsSourceServiceBean.java @@ -1,10 +1,15 @@ package dev.sheldan.oneplus.bot.modules.news.service; +import dev.sheldan.abstracto.core.service.FeatureModeService; import dev.sheldan.abstracto.core.service.PostTargetService; import dev.sheldan.abstracto.core.templating.model.MessageToSend; import dev.sheldan.abstracto.core.templating.service.TemplateService; import dev.sheldan.abstracto.core.utils.FutureUtils; +import dev.sheldan.oneplus.bot.modules.news.config.NewsFeatureDefinition; +import dev.sheldan.oneplus.bot.modules.news.config.NewsFeatureMode; import dev.sheldan.oneplus.bot.modules.news.config.NewsPostTarget; +import dev.sheldan.oneplus.bot.modules.news.model.ForumPostEntry; +import dev.sheldan.oneplus.bot.modules.news.model.ForumPostModel; import dev.sheldan.oneplus.bot.modules.news.model.ForumPostNotificationEntry; import dev.sheldan.oneplus.bot.modules.news.model.ForumPostNotificationModel; import dev.sheldan.oneplus.bot.modules.news.model.database.NewsForumPost; @@ -13,11 +18,14 @@ import dev.sheldan.oneplus.bot.modules.news.model.forum.ForumPost; import dev.sheldan.oneplus.bot.modules.news.service.management.NewsForumPostManagementServiceBean; import dev.sheldan.oneplus.bot.modules.news.service.management.NewsSourceManagementServiceBean; import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.entities.Message; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.stream.Collectors; @@ -45,7 +53,11 @@ public class NewsSourceServiceBean { @Autowired private NewsSourceServiceBean self; + @Autowired + private FeatureModeService featureModeService; + private static final String NEWS_FORUM_POST_NOTIFICATION_TEMPLATE_KEY = "newsForumPost_notification"; + private static final String NEWS_FORUM_POST_TEMPLATE_KEY = "newsForumPost"; public void checkForNewThreads() { Long targetServerId = Long.parseLong(System.getenv(NEWS_FORUM_POST_NOTIFICATION_SERVER_ID_ENV_NAME)); @@ -54,34 +66,75 @@ public class NewsSourceServiceBean { if(newForumPosts.isEmpty()) { return; } - List entries = new ArrayList<>(); - newForumPosts.forEach(forumPost -> entries.add(ForumPostNotificationEntry.fromPost(forumPost))); - ForumPostNotificationModel model = ForumPostNotificationModel - .builder() - .entries(entries) - .build(); + if(featureModeService.featureModeActive(NewsFeatureDefinition.NEWS, targetServerId, NewsFeatureMode.AUTOMATIC_POST)) { + List entries = new ArrayList<>(); + newForumPosts.forEach(forumPost -> entries.add(ForumPostEntry.fromPost(forumPost))); + ForumPostModel model = ForumPostModel + .builder() + .entries(entries) + .build(); + MessageToSend messageToSend = templateService.renderEmbedTemplate(NEWS_FORUM_POST_TEMPLATE_KEY, model, targetServerId); + List> messageFutures = postTargetService.sendEmbedInPostTarget(messageToSend, NewsPostTarget.NEWS_TARGET, targetServerId); + FutureUtils.toSingleFutureGeneric(messageFutures) + .thenAccept(unused -> { + log.info("Sent news forum post notification."); + List> posts = entries + .stream() + .map(forumPostNotificationEntry -> Pair.of(forumPostNotificationEntry.getCreatorId(), forumPostNotificationEntry.getPostId())) + .toList(); + self.persistForumPostsAndThreadCount(posts); + self.handleAutomaticPublish(messageFutures, targetServerId); + }); + } else { + List entries = new ArrayList<>(); + newForumPosts.forEach(forumPost -> entries.add(ForumPostNotificationEntry.fromPost(forumPost))); + ForumPostNotificationModel model = ForumPostNotificationModel + .builder() + .entries(entries) + .build(); + MessageToSend messageToSend = templateService.renderEmbedTemplate(NEWS_FORUM_POST_NOTIFICATION_TEMPLATE_KEY, model, targetServerId); + FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, NewsPostTarget.FORUM_POST_NOTIFICATION, targetServerId)) + .thenAccept(unused -> { + log.info("Sent news forum post notification."); + List> posts = entries + .stream() + .map(forumPostNotificationEntry -> Pair.of(forumPostNotificationEntry.getCreatorId(), forumPostNotificationEntry.getPostId())) + .toList(); + self.persistForumPostsAndThreadCount(posts); + }).exceptionally(throwable -> { + log.error("Failed to send news forum post notification.", throwable); + return null; + }); + } - MessageToSend messageToSend = templateService.renderEmbedTemplate(NEWS_FORUM_POST_NOTIFICATION_TEMPLATE_KEY, model, targetServerId); - - FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, NewsPostTarget.FORUM_POST_NOTIFICATION, targetServerId)) - .thenAccept(unused -> { - log.info("Sent news forum post notification."); - self.persistForumPostsAndThreadCount(entries); - }).exceptionally(throwable -> { - log.error("Failed to send news forum post notification.", throwable); - return null; - }); } @Transactional - public void persistForumPostsAndThreadCount(List entries) { + public CompletableFuture handleAutomaticPublish(List> messageFutures, Long serverId) { + if(featureModeService.featureModeActive(NewsFeatureDefinition.NEWS, serverId, NewsFeatureMode.AUTOMATIC_PUBLISH)) { + if(messageFutures != null && !messageFutures.isEmpty() && messageFutures.get(0) != null) { + Message newsMessage = messageFutures.get(0).join(); + log.info("Publishing message {} in server {}.", newsMessage.getId(), serverId); + return newsMessage.crosspost().submit(); + } else { + log.info("No message found - not cross posting."); + return CompletableFuture.completedFuture(null); + } + } else { + log.info("Automatic publishing disabled in server {}.", serverId); + return CompletableFuture.completedFuture(null); + } + } + + @Transactional + public void persistForumPostsAndThreadCount(List> posts) { Map sourceMap = newsSourceManagementServiceBean.loadNewsSources() .stream() .collect(Collectors.toMap(NewsSource::getUserId, Function.identity())); - entries.forEach(forumPostNotificationEntry -> - newsForumPostManagementServiceBean.createPost(sourceMap.get(forumPostNotificationEntry.getCreatorId()), forumPostNotificationEntry.getPostId())); + posts.forEach(forumPostNotificationEntry -> + newsForumPostManagementServiceBean.createPost(sourceMap.get(forumPostNotificationEntry.getLeft()), forumPostNotificationEntry.getRight())); sourceMap.values().forEach(newsSource -> { Long currentThreadCount = forumApiClient.getCurrentThreadCount(newsSource); diff --git a/application/oneplus-bot-modules/news/src/main/resources/news.properties b/application/oneplus-bot-modules/news/src/main/resources/news.properties index 7a07559..05f7115 100644 --- a/application/oneplus-bot-modules/news/src/main/resources/news.properties +++ b/application/oneplus-bot-modules/news/src/main/resources/news.properties @@ -7,6 +7,14 @@ abstracto.featureFlags.news.enabled=false abstracto.feature.news.removalDays=4 abstracto.feature.news.postLockSeconds=3600 +abstracto.featureModes.automaticPublish.featureName=news +abstracto.featureModes.automaticPublish.mode=automaticPublish +abstracto.featureModes.automaticPublish.enabled=false + +abstracto.featureModes.automaticPost.featureName=news +abstracto.featureModes.automaticPost.mode=automaticPost +abstracto.featureModes.automaticPost.enabled=false + abstracto.feature.news.userURL=https://community.oneplus.com/ajax/user/frontend/user/info?uid=%s # TODO support pagination.. eventually abstracto.feature.news.threadURL=https://community.oneplus.com/ajax/user/frontend/thread/page?page=1&limit=100&uid=%s \ No newline at end of file diff --git a/templates/oneplus-bot-templates/module-templates/news-templates/src/main/resources/default/job/newsForumPost_embed.ftl b/templates/oneplus-bot-templates/module-templates/news-templates/src/main/resources/default/job/newsForumPost_embed.ftl new file mode 100644 index 0000000..9c930e0 --- /dev/null +++ b/templates/oneplus-bot-templates/module-templates/news-templates/src/main/resources/default/job/newsForumPost_embed.ftl @@ -0,0 +1,17 @@ +{ + <#assign roleMention="<@&479202891358535681>"/> + "additionalMessage": "${roleMention}", + "embeds": [ + { + <#macro postDisplay post> + ${post.subject?json_string} + https://community.oneplus.com/thread/${post.postId?c} + + + "description": "<#list entries as entry><@postDisplay post=entry />" + } + ], + "messageConfig": { + "allowsRoleMention": true + } +} \ No newline at end of file