diff --git a/stbf-api/src/main/java/ru/penkrat/stbf/api/Media.java b/stbf-api/src/main/java/ru/penkrat/stbf/api/Media.java index 34cbaba..fe2334a 100644 --- a/stbf-api/src/main/java/ru/penkrat/stbf/api/Media.java +++ b/stbf-api/src/main/java/ru/penkrat/stbf/api/Media.java @@ -4,6 +4,7 @@ import lombok.Builder; import lombok.Getter; import lombok.ToString; +import java.util.function.Consumer; import java.util.function.Supplier; @Getter @@ -25,4 +26,6 @@ public class Media { private Integer height; + private Consumer fileIdConsumer; + } diff --git a/stbf-common/src/main/java/ru/penkrat/stbf/common/screen/RamFileIdStorageMediaScreen.java b/stbf-common/src/main/java/ru/penkrat/stbf/common/screen/RamFileIdStorageMediaScreen.java new file mode 100644 index 0000000..cee5929 --- /dev/null +++ b/stbf-common/src/main/java/ru/penkrat/stbf/common/screen/RamFileIdStorageMediaScreen.java @@ -0,0 +1,85 @@ +package ru.penkrat.stbf.common.screen; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import ru.penkrat.stbf.api.Keyboard; +import ru.penkrat.stbf.api.Media; +import ru.penkrat.stbf.api.Screen; +import ru.penkrat.stbf.api.ScreenProperties; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Supplier; + +@Slf4j +@RequiredArgsConstructor +public class RamFileIdStorageMediaScreen implements Screen { + + private static final Map STORAGE = new ConcurrentHashMap<>(); + + private final Screen wrapped; + + private Media wrappedMedia; + + @Override + public Media getMedia() { + if (wrapped.getMedia() == null) { + return null; + } + if (wrappedMedia != null) { + return wrappedMedia; + } + + final String url = wrapped.getMedia().getUrl(); + wrappedMedia = Media.builder() + .data(getData(url)) + .url(getUrl(url)) + .fileId(STORAGE.get(url)) + .fileIdConsumer(saveFileId(url)) + .mediaType(wrapped.getMedia().getMediaType()) + .duration(wrapped.getMedia().getDuration()) + .height(wrapped.getMedia().getHeight()) + .width(wrapped.getMedia().getWidth()) + .duration(wrapped.getMedia().getDuration()) + .build(); + + return wrappedMedia; + } + + protected String getUrl(String url) { + return STORAGE.containsKey(url) ? null : url; + } + + protected Supplier getData(String url) { + return null; + } + + @Override + public String getText() { + return wrapped.getText(); + } + + @Override + public Keyboard getKeyboard() { + return wrapped.getKeyboard(); + } + + @Override + public ScreenProperties getScreenProperties() { + return wrapped.getScreenProperties(); + } + + private Consumer saveFileId(String srcUrl) { + return fileId -> { + if (STORAGE.containsKey(srcUrl)) { + return; + } + log.debug("Store {} as {}", srcUrl, fileId); + + STORAGE.put(srcUrl, fileId); + wrappedMedia = null; + }; + } + +} diff --git a/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/BotResponseImpl.java b/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/BotResponseImpl.java index fd7fb4e..2d5a451 100644 --- a/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/BotResponseImpl.java +++ b/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/BotResponseImpl.java @@ -11,6 +11,7 @@ import com.pengrad.telegrambot.request.AbstractSendRequest; import com.pengrad.telegrambot.request.DeleteMessage; import com.pengrad.telegrambot.request.EditMessageText; import com.pengrad.telegrambot.request.SendDocument; +import com.pengrad.telegrambot.response.SendResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import ru.penkrat.stbf.api.BotResponse; @@ -28,7 +29,7 @@ public class BotResponseImpl implements BotResponse { public void send(Screen screen) { log.debug("Send message: \n============\n{}\n============", screen.getText().trim()); - AbstractSendRequest sendMessage= SendMethodUtils.createFromScreen(chatId(), screen); + AbstractSendRequest sendMessage = SendMethodUtils.createFromScreen(chatId(), screen); if (screen.getKeyboard() instanceof KeyboardImpl) { KeyboardImpl kk = (KeyboardImpl) screen.getKeyboard(); @@ -48,7 +49,8 @@ public class BotResponseImpl implements BotResponse { } } - telegramBot.execute(sendMessage); + final SendResponse sendResponse = telegramBot.execute(sendMessage); + SendMethodUtils.processResponse(screen, sendResponse); } @Override diff --git a/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/SendMethodUtils.java b/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/SendMethodUtils.java index 85b8836..be67852 100644 --- a/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/SendMethodUtils.java +++ b/stbf-pengrad/src/main/java/ru/penkrat/stbf/impl/pengrad/SendMethodUtils.java @@ -1,16 +1,24 @@ package ru.penkrat.stbf.impl.pengrad; +import com.pengrad.telegrambot.model.Message; import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.request.AbstractSendRequest; +import com.pengrad.telegrambot.request.SendAnimation; +import com.pengrad.telegrambot.request.SendAudio; import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendPhoto; import com.pengrad.telegrambot.request.SendVideo; +import com.pengrad.telegrambot.request.SendVoice; +import com.pengrad.telegrambot.response.SendResponse; import lombok.NonNull; +import lombok.RequiredArgsConstructor; import lombok.experimental.UtilityClass; +import org.jetbrains.annotations.NotNull; import ru.penkrat.stbf.api.Media; import ru.penkrat.stbf.api.Screen; -import java.util.function.Function; +import java.util.function.BiFunction; +import java.util.function.Consumer; @UtilityClass class SendMethodUtils { @@ -19,23 +27,51 @@ class SendMethodUtils { if (isMedia(screen)) { final Media media = screen.getMedia(); switch (media.getMediaType()) { + case ANIMATION: + return Setter.to(createSendAnimation(chatId, media)) + .setNotNUll(SendAnimation::caption, screen.getText()) + .setNotNUll(SendAnimation::width, media.getWidth()) + .setNotNUll(SendAnimation::height, media.getHeight()) + .setNotNUll(SendAnimation::duration, media.getDuration()) + .set(SendAnimation::parseMode, screen.getScreenProperties().isParseModeHtml() + ? ParseMode.HTML + : ParseMode.MarkdownV2) + .get(); + case AUDIO: + return Setter.to(createSendAudio(chatId, media)) + .setNotNUll(SendAudio::caption, screen.getText()) + .setNotNUll(SendAudio::duration, media.getDuration()) + .set(SendAudio::parseMode, screen.getScreenProperties().isParseModeHtml() + ? ParseMode.HTML + : ParseMode.MarkdownV2) + .get(); case PHOTO: - final SendPhoto sendPhoto = new SendPhoto(chatId, media.getUrl()); - apply(sendPhoto, sendPhoto::caption, screen.getText()); - sendPhoto.parseMode(screen.getScreenProperties().isParseModeHtml() - ? ParseMode.HTML - : ParseMode.MarkdownV2); - return sendPhoto; + return Setter.to(createSendPhoto(chatId, media)) + .setNotNUll(SendPhoto::caption, screen.getText()) + .set(SendPhoto::parseMode, screen.getScreenProperties().isParseModeHtml() + ? ParseMode.HTML + : ParseMode.MarkdownV2) + .get(); case VIDEO: - final SendVideo sendVideo = new SendVideo(chatId, media.getUrl()); - apply(sendVideo, sendVideo::caption, screen.getText()); - apply(sendVideo, sendVideo::width, media.getWidth()); - apply(sendVideo, sendVideo::height, media.getHeight()); - apply(sendVideo, sendVideo::duration, media.getDuration()); - sendVideo.parseMode(screen.getScreenProperties().isParseModeHtml() - ? ParseMode.HTML - : ParseMode.MarkdownV2); - return sendVideo; + return Setter.to(createSendVideo(chatId, media)) + .setNotNUll(SendVideo::caption, screen.getText()) + .setNotNUll(SendVideo::width, media.getWidth()) + .setNotNUll(SendVideo::height, media.getHeight()) + .setNotNUll(SendVideo::duration, media.getDuration()) + .set(SendVideo::parseMode, screen.getScreenProperties().isParseModeHtml() + ? ParseMode.HTML + : ParseMode.MarkdownV2) + .get(); + case VOICE: + return Setter.to(createSendVoice(chatId, media)) + .setNotNUll(SendVoice::caption, screen.getText()) + .setNotNUll(SendVoice::duration, media.getDuration()) + .set(SendVoice::parseMode, screen.getScreenProperties().isParseModeHtml() + ? ParseMode.HTML + : ParseMode.MarkdownV2) + .get(); + default: + throw new IllegalStateException("Unsupported media type " + media.getMediaType()); } } @@ -45,14 +81,106 @@ class SendMethodUtils { .disableNotification(screen.getScreenProperties().isDisableNotification()); } + + public static void processResponse(Screen screen, SendResponse sendResponse) { + if (isMedia(screen)) { + final Consumer fileIdConsumer = screen.getMedia().getFileIdConsumer(); + if (fileIdConsumer != null) { + final Message message = sendResponse.message(); + if (message.animation() != null) + fileIdConsumer.accept(message.animation().fileId()); + else if (message.audio() != null) + fileIdConsumer.accept(message.audio().fileId()); + else if (message.photo() != null && message.photo().length > 0) + fileIdConsumer.accept(message.photo()[0].fileId()); + else if (message.video() != null) + fileIdConsumer.accept(message.video().fileId()); + else if (message.voice() != null) + fileIdConsumer.accept(message.voice().fileId()); + } + } + } + + @NotNull + private SendVoice createSendVoice(@NotNull Object chatId, Media media) { + if (media.getData() != null) { + return new SendVoice(chatId, media.getData().get()); + } + if (media.getFileId() != null) { + return new SendVoice(chatId, media.getFileId()); + } + return new SendVoice(chatId, media.getUrl()); + } + + @NotNull + private SendVideo createSendVideo(@NotNull Object chatId, Media media) { + if (media.getData() != null) { + return new SendVideo(chatId, media.getData().get()); + } + if (media.getFileId() != null) { + return new SendVideo(chatId, media.getFileId()); + } + return new SendVideo(chatId, media.getUrl()); + } + + @NotNull + private SendAudio createSendAudio(@NotNull Object chatId, Media media) { + if (media.getData() != null) { + return new SendAudio(chatId, media.getData().get()); + } + if (media.getFileId() != null) { + return new SendAudio(chatId, media.getFileId()); + } + return new SendAudio(chatId, media.getUrl()); + } + + @NotNull + private SendAnimation createSendAnimation(@NotNull Object chatId, Media media) { + if (media.getData() != null) { + return new SendAnimation(chatId, media.getData().get()); + } + if (media.getFileId() != null) { + return new SendAnimation(chatId, media.getFileId()); + } + return new SendAnimation(chatId, media.getUrl()); + } + + @NotNull + private SendPhoto createSendPhoto(@NotNull Object chatId, Media media) { + if (media.getData() != null) { + return new SendPhoto(chatId, media.getData().get()); + } + if (media.getFileId() != null) { + return new SendPhoto(chatId, media.getFileId()); + } + return new SendPhoto(chatId, media.getUrl()); + } + private boolean isMedia(Screen screen) { return screen.getMedia() != null; } - private T apply(T target, Function setter, V value) { - if (value != null) { - setter.apply(value); + + @RequiredArgsConstructor(staticName = "to") + private static class Setter { + + private final T target; + + Setter setNotNUll(BiFunction setter, V value) { + if (value != null) { + setter.apply(target, value); + } + return this; } - return target; + + Setter set(BiFunction setter, V value) { + setter.apply(target, value); + return this; + } + + T get() { + return target; + } + } } diff --git a/stbf-templates/src/main/java/ru/penkrat/stbf/templates/xml/FlowScreenResolverDelegate.java b/stbf-templates/src/main/java/ru/penkrat/stbf/templates/xml/FlowScreenResolverDelegate.java index 94bce15..5730d1a 100644 --- a/stbf-templates/src/main/java/ru/penkrat/stbf/templates/xml/FlowScreenResolverDelegate.java +++ b/stbf-templates/src/main/java/ru/penkrat/stbf/templates/xml/FlowScreenResolverDelegate.java @@ -9,6 +9,7 @@ import ru.penkrat.stbf.api.KeyboardBuilder; import ru.penkrat.stbf.api.Media; import ru.penkrat.stbf.api.Screen; import ru.penkrat.stbf.common.screen.MediaScreen; +import ru.penkrat.stbf.common.screen.RamFileIdStorageMediaScreen; import ru.penkrat.stbf.common.screen.TextScreen; import ru.penkrat.stbf.templates.ActionResolver; import ru.penkrat.stbf.templates.KeyboardProvider; @@ -45,7 +46,8 @@ class FlowScreenResolverDelegate implements ScreenResolver { if (StringUtils.isNotEmpty(item.getMediaRef())) { final Media media = mediaResolver.getMedia(item.getMediaRef()); - return new MediaScreen(item.getText(), media, buildKeyboard(item.getKeyboard(), null)); + final MediaScreen mediaScreen = new MediaScreen(item.getText(), media, buildKeyboard(item.getKeyboard(), null)); + return new RamFileIdStorageMediaScreen(mediaScreen); } return new TextScreen(item.getText(), buildKeyboard(item.getKeyboard(), null)); @@ -57,8 +59,9 @@ class FlowScreenResolverDelegate implements ScreenResolver { if (StringUtils.isNotEmpty(item.getMediaRef())) { final Media media = mediaResolver.getMedia(item.getMediaRef()); - return new MediaScreen(templateRenderer.render(item.getText(), context), media, + final MediaScreen mediaScreen = new MediaScreen(templateRenderer.render(item.getText(), context), media, resolveKeyboard(item.getKeyboard(), context)); + return new RamFileIdStorageMediaScreen(mediaScreen); } return new TextScreen(templateRenderer.render(item.getText(), context),