From 9629779dcc051924d2a24258d768722bd3e19615 Mon Sep 17 00:00:00 2001 From: Yevhen Vasyliev Date: Wed, 8 Nov 2023 16:00:38 +0200 Subject: [PATCH] optimized delay management for sending posts --- .github/workflows/release.yml | 3 + .../com/github/yvasyliev/Application.java | 38 +++--------- .../appenders/TelegramBotAppender.java | 53 +++++++++-------- .../yvasyliev/bots/telegram/RedTelBot.java | 16 +++++ .../async/DelayedBlockingExecutor.java | 35 +++++++++++ .../service/data/BlockedAuthorService.java | 6 +- .../deserializers/PostDeserializer.java | 10 +--- .../service/reddit/RedditVideoDownloader.java | 1 + .../service/telegram/PostManager.java | 52 +++-------------- .../telegram/ScheduledPostManager.java | 44 ++++++++++++++ .../telegram/commands/PausePublishing.java | 7 ++- .../telegram/commands/ResumePublishing.java | 7 ++- .../service/telegram/commands/Stop.java | 7 +-- .../telegram/posts/PhotoGroupPostService.java | 58 ++++--------------- src/main/resources/application.properties | 9 ++- src/main/resources/log4j2.xml | 6 +- .../tc/RedditPhotoGroupPostTest.java | 8 --- src/test/resources/log4j2.xml | 8 ++- 18 files changed, 188 insertions(+), 180 deletions(-) create mode 100644 src/main/java/com/github/yvasyliev/service/async/DelayedBlockingExecutor.java create mode 100644 src/main/java/com/github/yvasyliev/service/telegram/ScheduledPostManager.java diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c61105d..f49089b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,10 +31,13 @@ jobs: - name: Run test run: mvn test env: + spring.jpa.hibernate.ddl-auto: create-drop telegram.admin.id: ${{ secrets.telegram.admin.id }} telegram.bot.token: ${{ secrets.test.telegram.bot.token }} telegram.channel.id: ${{ secrets.test.telegram.channel.id }} telegram.chat.id: ${{ secrets.test.telegram.chat.id }} + telegram.schedule.posting.enabled: false + release: runs-on: ubuntu-latest diff --git a/src/main/java/com/github/yvasyliev/Application.java b/src/main/java/com/github/yvasyliev/Application.java index 3feb0f8..b22c4aa 100644 --- a/src/main/java/com/github/yvasyliev/Application.java +++ b/src/main/java/com/github/yvasyliev/Application.java @@ -13,28 +13,25 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.ApplicationPidFileWriter; -import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Scope; +import org.springframework.scheduling.annotation.EnableScheduling; import org.telegram.telegrambots.meta.TelegramBotsApi; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; import org.telegram.telegrambots.starter.TelegramBotStarterConfiguration; import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; -import java.io.File; import java.net.http.HttpClient; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; @SpringBootApplication +@EnableScheduling public class Application extends TelegramBotStarterConfiguration { - private static ConfigurableApplicationContext context; + private static ApplicationContext context; public static void main(String[] args) { context = new SpringApplicationBuilder(Application.class) @@ -43,8 +40,10 @@ public static void main(String[] args) { .run(args); } - public static ConfigurableApplicationContext getContext() { - return context; + public static void withContext(Consumer contextConsumer) { + if (context != null) { + contextConsumer.accept(context); + } } @Bean("/help") @@ -71,11 +70,6 @@ public Module module(JsonDeserializer postJsonDeserializer) { return module; } - @Bean - public ScheduledExecutorService scheduledExecutorService() { - return Executors.newSingleThreadScheduledExecutor(); - } - @Bean public String userAgent(@Value("${REDDIT_USERNAME}") String redditUsername) { return "java:reddit-telegram-repeater:2.0.0 (by /u/%s)".formatted(redditUsername); @@ -86,11 +80,6 @@ public HttpClient httpClient() { return HttpClient.newHttpClient(); } - @Bean - public File stateSrc() { - return new File("state.json"); - } - @Bean @Scope(BeanDefinition.SCOPE_PROTOTYPE) public Map synchronizedFixedSizeMap(@Value("16") int maxSize) { @@ -126,13 +115,4 @@ public Map longExternalMessageDataMap(@Value("16") in return synchronizedFixedSizeMap(maxSize); } - @Bean - public Executor delayedExecutor() { - return CompletableFuture.delayedExecutor(10, TimeUnit.SECONDS, singleThreadExecutor()); - } - - @Bean - public Executor singleThreadExecutor() { - return Executors.newSingleThreadExecutor(); - } } diff --git a/src/main/java/com/github/yvasyliev/appenders/TelegramBotAppender.java b/src/main/java/com/github/yvasyliev/appenders/TelegramBotAppender.java index 39f6e75..fe97d76 100644 --- a/src/main/java/com/github/yvasyliev/appenders/TelegramBotAppender.java +++ b/src/main/java/com/github/yvasyliev/appenders/TelegramBotAppender.java @@ -19,9 +19,13 @@ import java.io.PrintWriter; import java.io.Serializable; import java.io.StringWriter; +import java.util.Optional; @Plugin(name = "TelegramBotAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) public class TelegramBotAppender extends AbstractAppender { + private static final String MESSAGE_TEMPLATE = """ + %s + %s"""; protected TelegramBotAppender(String name, Filter filter, Layout layout, boolean ignoreExceptions, Property[] properties) { super(name, filter, layout, ignoreExceptions, properties); @@ -34,32 +38,33 @@ public static TelegramBotAppender createAppender(@PluginAttribute("name") String @Override public void append(LogEvent event) { - var formattedMessage = event.getMessage().getFormattedMessage(); - - var stackTrace = getStackTrace(event.getThrown()); - if (stackTrace != null) { - formattedMessage = """ - %s - %s""".formatted(formattedMessage, stackTrace); - } - - try { - Application.getContext().getBean(TelegramNotifier.class).applyWithException(formattedMessage); - } catch (Exception e) { - e.printStackTrace(System.err); - } + Application.withContext(applicationContext -> { + try { + applicationContext + .getBean(TelegramNotifier.class) + .applyWithException(buildMessage(event)); + } catch (Exception e) { + e.printStackTrace(System.err); + } + }); } - private String getStackTrace(Throwable throwable) { - if (throwable == null) { - return null; - } + private String buildMessage(LogEvent event) { + var formattedMessage = event.getMessage().getFormattedMessage(); + return getStackTrace(event.getThrown()) + .map(stackTrace -> MESSAGE_TEMPLATE.formatted(formattedMessage, stackTrace)) + .orElse(formattedMessage); + } - try (var stringWriter = new StringWriter(); var printWriter = new PrintWriter(stringWriter)) { - throwable.printStackTrace(printWriter); - return stringWriter.toString(); - } catch (IOException e) { - return "Failed to read stack trace: " + e; - } + private Optional getStackTrace(Throwable throwable) { + return Optional.ofNullable(throwable).map(t -> { + try (var stringWriter = new StringWriter(); var printWriter = new PrintWriter(stringWriter)) { + t.printStackTrace(printWriter); + return stringWriter.toString(); + } catch (IOException e) { + e.printStackTrace(System.err); + return null; + } + }); } } diff --git a/src/main/java/com/github/yvasyliev/bots/telegram/RedTelBot.java b/src/main/java/com/github/yvasyliev/bots/telegram/RedTelBot.java index 68a5d9b..ee35bb2 100644 --- a/src/main/java/com/github/yvasyliev/bots/telegram/RedTelBot.java +++ b/src/main/java/com/github/yvasyliev/bots/telegram/RedTelBot.java @@ -6,6 +6,7 @@ import com.github.yvasyliev.model.dto.ChannelPost; import com.github.yvasyliev.model.dto.ExternalMessageData; import com.github.yvasyliev.model.events.NewChannelPostEvent; +import com.github.yvasyliev.service.async.DelayedBlockingExecutor; import com.github.yvasyliev.service.telegram.callbacks.Callback; import com.github.yvasyliev.service.telegram.commands.Command; import jakarta.annotation.PreDestroy; @@ -17,6 +18,8 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; import org.telegram.telegrambots.meta.TelegramBotsApi; +import org.telegram.telegrambots.meta.api.methods.send.SendMediaGroup; +import org.telegram.telegrambots.meta.api.methods.send.SendPhoto; import org.telegram.telegrambots.meta.api.objects.CallbackQuery; import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.api.objects.Update; @@ -25,8 +28,10 @@ import org.telegram.telegrambots.meta.generics.BotSession; import org.telegram.telegrambots.starter.AfterBotRegistration; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.CompletableFuture; @Component public class RedTelBot extends AbstractRedTelBot { @@ -49,6 +54,9 @@ public class RedTelBot extends AbstractRedTelBot { @Autowired private ApplicationEventPublisher eventPublisher; + @Autowired + private DelayedBlockingExecutor delayedBlockingExecutor; + public RedTelBot(@Value("${telegram.bot.token}") String botToken) { super(botToken); } @@ -140,6 +148,14 @@ public boolean isAdmin(User user) { return user.getId().toString().equals(getAdminId()); } + public CompletableFuture> executeDelayed(SendMediaGroup sendMediaGroup) { + return delayedBlockingExecutor.submit(() -> execute(sendMediaGroup)); + } + + public CompletableFuture executeDelayed(SendPhoto sendPhoto) { + return delayedBlockingExecutor.submit(() -> execute(sendPhoto)); + } + private Optional getCommand(Message message) { long userId = message.getFrom().getId(); if (message.hasText()) { diff --git a/src/main/java/com/github/yvasyliev/service/async/DelayedBlockingExecutor.java b/src/main/java/com/github/yvasyliev/service/async/DelayedBlockingExecutor.java new file mode 100644 index 0000000..df4f628 --- /dev/null +++ b/src/main/java/com/github/yvasyliev/service/async/DelayedBlockingExecutor.java @@ -0,0 +1,35 @@ +package com.github.yvasyliev.service.async; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Queue; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +@Component +public class DelayedBlockingExecutor { + @Value("#{new java.util.concurrent.LinkedBlockingQueue()}") + private Queue delayedRunnables; + + public CompletableFuture submit(Callable callable) { + var completableFuture = new CompletableFuture(); + delayedRunnables.add(() -> { + try { + completableFuture.complete(callable.call()); + } catch (Exception e) { + completableFuture.completeExceptionally(e); + } + }); + return completableFuture; + } + + @Scheduled(fixedDelayString = "${delayed.blocking.executor.delay.in.seconds:20}", timeUnit = TimeUnit.SECONDS) + public void runNextRunnable() { + if (!delayedRunnables.isEmpty()) { + delayedRunnables.poll().run(); + } + } +} diff --git a/src/main/java/com/github/yvasyliev/service/data/BlockedAuthorService.java b/src/main/java/com/github/yvasyliev/service/data/BlockedAuthorService.java index 8397b92..92bc500 100644 --- a/src/main/java/com/github/yvasyliev/service/data/BlockedAuthorService.java +++ b/src/main/java/com/github/yvasyliev/service/data/BlockedAuthorService.java @@ -6,15 +6,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.List; - @Service public class BlockedAuthorService { @Autowired private BlockedAuthorRepository blockedAuthorRepository; - public List findAll() { - return blockedAuthorRepository.findAll(); + public boolean isBlocked(String username) { + return blockedAuthorRepository.existsById(username); } @Transactional diff --git a/src/main/java/com/github/yvasyliev/service/deserializers/PostDeserializer.java b/src/main/java/com/github/yvasyliev/service/deserializers/PostDeserializer.java index 3f046e0..d83df16 100644 --- a/src/main/java/com/github/yvasyliev/service/deserializers/PostDeserializer.java +++ b/src/main/java/com/github/yvasyliev/service/deserializers/PostDeserializer.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.github.yvasyliev.model.dto.post.Post; -import com.github.yvasyliev.model.entities.BlockedAuthor; import com.github.yvasyliev.service.data.BlockedAuthorService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -30,12 +29,7 @@ public Post deserialize(JsonParser jsonParser, DeserializationContext deserializ var jsonPost = jsonParser.readValueAs(JsonNode.class); var author = jsonPost.get("author").textValue(); var created = jsonPost.get("created").intValue(); - var postUrl = jsonPost.has("url_overridden_by_dest") ? jsonPost.get("url_overridden_by_dest").textValue() : jsonPost.get("url").textValue(); - var blockedAuthors = blockedAuthorService - .findAll() - .stream() - .map(BlockedAuthor::getUsername) - .toList(); + var postUrl = jsonPost.has("url_overridden_by_dest") ? jsonPost.get("url_overridden_by_dest").textValue() : jsonPost.get("url").textValue(); // TODO: 11/8/2023 simplify jsonPost = extractRootPost(jsonPost); for (var postMapper : postMappers) { @@ -43,7 +37,7 @@ public Post deserialize(JsonParser jsonParser, DeserializationContext deserializ var optionalPost = postMapper.applyWithException(jsonPost).map(post -> { post.setAuthor(author); post.setCreated(created); - post.setApproved(!blockedAuthors.contains(author)); + post.setApproved(!blockedAuthorService.isBlocked(author)); post.setPostUrl(postUrl); return post; }); diff --git a/src/main/java/com/github/yvasyliev/service/reddit/RedditVideoDownloader.java b/src/main/java/com/github/yvasyliev/service/reddit/RedditVideoDownloader.java index de77c2d..9bc891a 100644 --- a/src/main/java/com/github/yvasyliev/service/reddit/RedditVideoDownloader.java +++ b/src/main/java/com/github/yvasyliev/service/reddit/RedditVideoDownloader.java @@ -7,6 +7,7 @@ import java.io.IOException; +// TODO: 11/7/2023 remove Jsoup @Service public class RedditVideoDownloader implements ThrowingFunction { @Override diff --git a/src/main/java/com/github/yvasyliev/service/telegram/PostManager.java b/src/main/java/com/github/yvasyliev/service/telegram/PostManager.java index 3f58d48..9caa1a2 100644 --- a/src/main/java/com/github/yvasyliev/service/telegram/PostManager.java +++ b/src/main/java/com/github/yvasyliev/service/telegram/PostManager.java @@ -7,7 +7,6 @@ import com.github.yvasyliev.model.dto.post.Post; import com.github.yvasyliev.model.events.NewChannelPostEvent; import com.github.yvasyliev.service.data.RedditTelegramForwarderPropertyService; -import com.github.yvasyliev.service.reddit.SubredditPostSupplier; import com.github.yvasyliev.service.telegram.posts.PhotoGroupPostService; import com.github.yvasyliev.service.telegram.posts.PostService; import org.slf4j.Logger; @@ -24,20 +23,12 @@ import java.util.List; import java.util.Map; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.ExecutionException; @Service public class PostManager { private static final Logger LOGGER = LoggerFactory.getLogger(PostManager.class); - @Autowired - private ScheduledExecutorService executorService; - - @Autowired - private SubredditPostSupplier subredditPostSupplier; - @Autowired private RedTelBot redTelBot; @@ -56,22 +47,6 @@ public class PostManager { @Autowired private PhotoGroupPostService photoGroupPostService; - private final AtomicBoolean publishing = new AtomicBoolean(true); - - public void schedulePosting() { - executorService.scheduleWithFixedDelay(() -> { - if (publishing.get()) { - try { - var newPosts = subredditPostSupplier.getWithException(); - publishPosts(newPosts); - } catch (Exception e) { - LOGGER.error("Failed to find new posts.", e); - } - } - }, 0, 1, TimeUnit.MINUTES); - LOGGER.info("Started posting."); - } - public void publishPosts(List posts) { posts.forEach(this::publishPost); } @@ -80,8 +55,6 @@ public void publishPost(T post) { if (context.containsBean(post.getType())) { try { publishPost(post, post.getType()); - } catch (TelegramApiException | JsonProcessingException e) { - LOGGER.error("Failed to ask approve.", e); } catch (Exception e) { LOGGER.error("Failed to send post: {}", post, e); } @@ -94,29 +67,22 @@ public void publishPost(T post, String postServiceName) throws var chatId = post.isApproved() ? redTelBot.getChannelId() : redTelBot.getAdminId(); var sentMessage = postService.applyWithException(chatId, post); if (sentMessage.isPresent() && !post.isApproved()) { - askApprove(chatId, post.getCreated()); - postCandidates.put(post.getCreated(), post); + try { + askApprove(chatId, post.getCreated()); + postCandidates.put(post.getCreated(), post); + } catch (TelegramApiException | JsonProcessingException e) { + LOGGER.error("Failed to ask approve.", e); + } } } - public void pausePublishing() { - publishing.set(false); - } - - public void resumePublishing() { - publishing.set(true); - } - - public void stopPublishing() { - executorService.shutdown(); - } - + // TODO: 11/8/2023 Move to PhotoGroupPostService @EventListener public void onNewChannelPostEvent(NewChannelPostEvent newChannelPostEvent) { var channelPost = newChannelPostEvent.getChannelPost(); try { photoGroupPostService.sendExtraPhotos(channelPost.messageId(), channelPost.forwardFromMessageId()); - } catch (TelegramApiException e) { + } catch (ExecutionException | InterruptedException e) { LOGGER.error("Failed to send extra photos.", e); } } diff --git a/src/main/java/com/github/yvasyliev/service/telegram/ScheduledPostManager.java b/src/main/java/com/github/yvasyliev/service/telegram/ScheduledPostManager.java new file mode 100644 index 0000000..7d018c3 --- /dev/null +++ b/src/main/java/com/github/yvasyliev/service/telegram/ScheduledPostManager.java @@ -0,0 +1,44 @@ +package com.github.yvasyliev.service.telegram; + +import com.github.yvasyliev.service.reddit.SubredditPostSupplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +@Service +public class ScheduledPostManager extends PostManager { + private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledPostManager.class); + + @Value("#{new java.util.concurrent.atomic.AtomicBoolean(${telegram.schedule.posting.enabled:true})}") + private AtomicBoolean isPosting; + + @Autowired + private SubredditPostSupplier subredditPostSupplier; + + + @Scheduled(fixedDelayString = "${telegram.schedule.posting.delay.in.minutes:1}", timeUnit = TimeUnit.MINUTES) + public void shareNewPosts() { + if (isPosting.get()) { + try { + var newPosts = subredditPostSupplier.getWithException(); + publishPosts(newPosts); + } catch (Exception e) { + LOGGER.error("Failed to find new posts.", e); + } + } + } + + public void pausePosting() { + isPosting.set(false); + } + + public void resumePosting() { + isPosting.set(true); + } +} diff --git a/src/main/java/com/github/yvasyliev/service/telegram/commands/PausePublishing.java b/src/main/java/com/github/yvasyliev/service/telegram/commands/PausePublishing.java index 49b2cac..a3e2026 100644 --- a/src/main/java/com/github/yvasyliev/service/telegram/commands/PausePublishing.java +++ b/src/main/java/com/github/yvasyliev/service/telegram/commands/PausePublishing.java @@ -1,6 +1,6 @@ package com.github.yvasyliev.service.telegram.commands; -import com.github.yvasyliev.service.telegram.PostManager; +import com.github.yvasyliev.service.telegram.ScheduledPostManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; @@ -10,14 +10,15 @@ import java.io.IOException; import java.net.URISyntaxException; +// TODO: 11/8/2023 rename to pauseposting @Service("/pausepublishing") public class PausePublishing extends AdminCommand { @Autowired - private PostManager postManager; + private ScheduledPostManager postManager; @Override public void execute(Message message) throws TelegramApiException, URISyntaxException, IOException { - postManager.pausePublishing(); + postManager.pausePosting(); redTelBot.execute(new SendMessage( message.getChatId().toString(), responseReader.applyWithException("responses/pausepublishing.md") diff --git a/src/main/java/com/github/yvasyliev/service/telegram/commands/ResumePublishing.java b/src/main/java/com/github/yvasyliev/service/telegram/commands/ResumePublishing.java index 42af016..7a24132 100644 --- a/src/main/java/com/github/yvasyliev/service/telegram/commands/ResumePublishing.java +++ b/src/main/java/com/github/yvasyliev/service/telegram/commands/ResumePublishing.java @@ -1,6 +1,6 @@ package com.github.yvasyliev.service.telegram.commands; -import com.github.yvasyliev.service.telegram.PostManager; +import com.github.yvasyliev.service.telegram.ScheduledPostManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; @@ -10,14 +10,15 @@ import java.io.IOException; import java.net.URISyntaxException; +// TODO: 11/8/2023 rename to resumeposting @Service("/resumepublishing") public class ResumePublishing extends AdminCommand { @Autowired - private PostManager postManager; + private ScheduledPostManager postManager; @Override public void execute(Message message) throws TelegramApiException, URISyntaxException, IOException { - postManager.resumePublishing(); + postManager.resumePosting(); redTelBot.execute(new SendMessage( message.getChatId().toString(), responseReader.applyWithException("responses/resumepublishing.md") diff --git a/src/main/java/com/github/yvasyliev/service/telegram/commands/Stop.java b/src/main/java/com/github/yvasyliev/service/telegram/commands/Stop.java index 3183ce3..371bc71 100644 --- a/src/main/java/com/github/yvasyliev/service/telegram/commands/Stop.java +++ b/src/main/java/com/github/yvasyliev/service/telegram/commands/Stop.java @@ -1,7 +1,7 @@ package com.github.yvasyliev.service.telegram.commands; -import com.github.yvasyliev.service.telegram.PostManager; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Service; import org.telegram.telegrambots.meta.api.objects.Message; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; @@ -12,12 +12,11 @@ @Service("/stop") public class Stop extends AdminCommand { @Autowired - private PostManager postManager; + private ConfigurableApplicationContext context; @Override public void execute(Message message) throws TelegramApiException, URISyntaxException, IOException { - postManager.stopPublishing(); - redTelBot.stopPolling(); + context.close(); reply(message, "responses/stop.md", redTelBot.getBotUsername()); } } diff --git a/src/main/java/com/github/yvasyliev/service/telegram/posts/PhotoGroupPostService.java b/src/main/java/com/github/yvasyliev/service/telegram/posts/PhotoGroupPostService.java index 9a8eace..863a18e 100644 --- a/src/main/java/com/github/yvasyliev/service/telegram/posts/PhotoGroupPostService.java +++ b/src/main/java/com/github/yvasyliev/service/telegram/posts/PhotoGroupPostService.java @@ -23,10 +23,8 @@ import java.util.Map; import java.util.Optional; import java.util.Queue; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; +import java.util.function.Function; @Service(Post.Type.PHOTO_GROUP) public class PhotoGroupPostService extends PostService> { @@ -34,16 +32,16 @@ public class PhotoGroupPostService extends PostService extraPhotos; @Autowired - private Map extraPhotos; + private Function markdownV2Escaper; @Override @NonNull - public Optional> applyWithException(@NonNull String chatId, PhotoGroupPost post) throws TelegramApiException, URISyntaxException, IOException { + public Optional> applyWithException(@NonNull String chatId, PhotoGroupPost post) throws TelegramApiException, URISyntaxException, IOException, ExecutionException, InterruptedException { var pages = post.getPhotoUrlsPages(); - var text = post.getText(); + var text = markdownV2Escaper.apply(post.getText()); var hasSpoiler = post.isHasSpoiler(); var pagesAmount = pages.size(); @@ -54,7 +52,7 @@ public Optional> applyWithException(@NonNull String chatId, PhotoG var sendMediaGroup = sendMediaGroup(chatId, pages.element(), hasSpoiler, null); sendMediaGroup.getMedias().get(0).setCaption(caption); - var publishedPost = redTelBot.execute(sendMediaGroup); + var publishedPost = redTelBot.executeDelayed(sendMediaGroup).get(); var messages = new ArrayList<>(publishedPost); var messageId = publishedPost.get(0).getMessageId(); @@ -67,22 +65,22 @@ public Optional> applyWithException(@NonNull String chatId, PhotoG return Optional.of(messages); } - public List sendExtraPhotos(int replyToMessageId, int forwardMessageId) throws TelegramApiException { + public List sendExtraPhotos(int replyToMessageId, int forwardMessageId) throws ExecutionException, InterruptedException { var post = extraPhotos.remove(forwardMessageId); return post != null ? sendDelayed(redTelBot.getGroupId(), replyToMessageId, post) : List.of(); } - private List sendDelayed(String chatId, int replyToMessageId, PhotoGroupPost post) throws TelegramApiException { + private List sendDelayed(String chatId, int replyToMessageId, PhotoGroupPost post) throws ExecutionException, InterruptedException { var pages = new ArrayDeque<>(post.getPhotoUrlsPages()); var hasSpoiler = post.isHasSpoiler(); var messages = new ArrayList(); pages.removeFirst(); for (var page : pages) { messages.addAll(page.size() > 1 - ? executeDelayed(sendMediaGroup(chatId, page, hasSpoiler, replyToMessageId)) - : List.of(executeDelayed(sendPhoto(chatId, page.element(), hasSpoiler, replyToMessageId))) + ? redTelBot.executeDelayed(sendMediaGroup(chatId, page, hasSpoiler, replyToMessageId)).get() + : List.of(redTelBot.executeDelayed(sendPhoto(chatId, page.element(), hasSpoiler, replyToMessageId)).get()) ); } return messages; @@ -115,40 +113,4 @@ private SendPhoto sendPhoto(String chatId, String photo, boolean hasSpoiler, Int .replyToMessageId(replyToMessageId) .build(); } - - private List executeDelayed(SendMediaGroup sendMediaGroup) throws TelegramApiException { - try { - return CompletableFuture - .supplyAsync(() -> { - try { - return redTelBot.execute(sendMediaGroup); - } catch (TelegramApiException e) { - throw new CompletionException(e); - } - }, - delayedExecutor - ) - .get(); - } catch (InterruptedException | ExecutionException e) { - throw new TelegramApiException(e); - } - } - - public Message executeDelayed(SendPhoto sendPhoto) throws TelegramApiException { - try { - return CompletableFuture - .supplyAsync(() -> { - try { - return redTelBot.execute(sendPhoto); - } catch (TelegramApiException e) { - throw new CompletionException(e); - } - }, - delayedExecutor - ) - .get(); - } catch (InterruptedException | ExecutionException e) { - throw new TelegramApiException(e); - } - } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index af922f6..7de6fbd 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,6 +4,11 @@ spring.datasource.url=jdbc:sqlite:data.db spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.format_sql=true -spring.jpa.show-sql=true +spring.jpa.properties.hibernate.highlight_sql=true +#spring.jpa.show-sql=true -spring.main.web-application-type=none \ No newline at end of file +spring.main.web-application-type=none + +spring.output.ansi.enabled=always + +spring.task.scheduling.pool.size=2 \ No newline at end of file diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 98f469a..66d8857 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -2,7 +2,7 @@ - + - + + + \ No newline at end of file diff --git a/src/test/java/com/github/yvasyliev/tc/RedditPhotoGroupPostTest.java b/src/test/java/com/github/yvasyliev/tc/RedditPhotoGroupPostTest.java index 24483f7..a6e0834 100644 --- a/src/test/java/com/github/yvasyliev/tc/RedditPhotoGroupPostTest.java +++ b/src/test/java/com/github/yvasyliev/tc/RedditPhotoGroupPostTest.java @@ -2,11 +2,8 @@ import com.github.yvasyliev.model.dto.post.PhotoGroupPost; import com.github.yvasyliev.model.dto.post.Post; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import java.util.concurrent.TimeUnit; - import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -15,11 +12,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class RedditPhotoGroupPostTest extends AbstractRedditPostTest { - @AfterEach - void waitABit() throws InterruptedException { - TimeUnit.SECONDS.sleep(30); - } - @Test void postApproved() { assertDoesNotThrow(() -> blockedAuthorService.removeBlockedAuthor(photoGroupPost().getAuthor())); diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml index 431bb34..5375e9b 100644 --- a/src/test/resources/log4j2.xml +++ b/src/test/resources/log4j2.xml @@ -2,7 +2,7 @@ - + - + + + + +