#1 initial sending media files support
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
28
stbf-api/src/main/java/ru/penkrat/stbf/api/Media.java
Normal file
28
stbf-api/src/main/java/ru/penkrat/stbf/api/Media.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package ru.penkrat.stbf.api;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
@ToString(of = {"mediaType", "url"})
|
||||||
|
public class Media {
|
||||||
|
|
||||||
|
private MediaType mediaType;
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
private String fileId;
|
||||||
|
|
||||||
|
private Supplier<byte[]> data;
|
||||||
|
|
||||||
|
private Integer duration;
|
||||||
|
|
||||||
|
private Integer width;
|
||||||
|
|
||||||
|
private Integer height;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package ru.penkrat.stbf.api;
|
||||||
|
|
||||||
|
public enum MediaType {
|
||||||
|
ANIMATION,
|
||||||
|
AUDIO,
|
||||||
|
PHOTO,
|
||||||
|
VIDEO,
|
||||||
|
VOICE
|
||||||
|
}
|
||||||
@@ -2,13 +2,17 @@ package ru.penkrat.stbf.api;
|
|||||||
|
|
||||||
public interface Screen {
|
public interface Screen {
|
||||||
|
|
||||||
String getText();
|
String getText();
|
||||||
|
|
||||||
default Keyboard getKeyboard() {
|
default Media getMedia() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
default ScreenProperties getScreenProperties() {
|
default Keyboard getKeyboard() {
|
||||||
return ScreenProperties.DEFAULT;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default ScreenProperties getScreenProperties() {
|
||||||
|
return ScreenProperties.DEFAULT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package ru.penkrat.stbf.common.screen;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import ru.penkrat.stbf.api.Keyboard;
|
||||||
|
import ru.penkrat.stbf.api.Media;
|
||||||
|
import ru.penkrat.stbf.api.Screen;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class MediaScreen implements Screen {
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
private final Media media;
|
||||||
|
|
||||||
|
private final Keyboard keyboard;
|
||||||
|
|
||||||
|
public MediaScreen(String text, Media media) {
|
||||||
|
this(text, media, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,11 +1,18 @@
|
|||||||
<flow>
|
<flow>
|
||||||
|
<media>
|
||||||
|
<video id="40001" url="http://techslides.com/demos/sample-videos/small.mp4"/>
|
||||||
|
<photo id="40002" url="https://telegram.org/img/t_logo.png"/>
|
||||||
|
</media>
|
||||||
<actions>
|
<actions>
|
||||||
<action id="10001" name="start-action" command="/start">Start</action>
|
<action id="10001" name="start-action" command="/start">Start</action>
|
||||||
<action id="10002" name="help-action" command="/help">Help</action>
|
<action id="10002" name="help-action" command="/help">Help</action>
|
||||||
<action id="10003" name="to-inline-action" command="/inline">Inline</action>
|
<action id="10003" name="to-inline-action" command="/inline">Inline</action>
|
||||||
<action id="10004" name="inline1-action" callbackData="cmd:inline1">Inline button #1</action>
|
<action id="10004" name="inline1-action" callbackData="cmd:inline1">🔞 Inline button #1</action>
|
||||||
<action id="10005" name="inline2-action" callbackData="cmd:inline2">Inline button #2</action>
|
<action id="10005" name="inline2-action" callbackData="cmd:inline2">🐱 Inline button #2</action>
|
||||||
<action id="10006" name="url-action" url="https://git.penkrat.ru/ruslan/stbf">Git repo</action>
|
<action id="10006" name="url-action" url="https://git.penkrat.ru/ruslan/stbf">💻 Git repo</action>
|
||||||
|
<action id="10007" name="photo-action" callbackData="cmd:sendPhoto">🖼 My photo</action>
|
||||||
|
<action id="10008" name="video-action" callbackData="cmd:sendVideo">🎞 My video</action>
|
||||||
|
<action id="10009" name="to-inline-back-action" callbackData="cmd:inline">🔙 Back</action>
|
||||||
</actions>
|
</actions>
|
||||||
<screens>
|
<screens>
|
||||||
<screen id="20001" name="on-start-screen">
|
<screen id="20001" name="on-start-screen">
|
||||||
@@ -35,6 +42,10 @@
|
|||||||
<button actionRef="inline1-action"></button>
|
<button actionRef="inline1-action"></button>
|
||||||
<button actionRef="inline2-action"></button>
|
<button actionRef="inline2-action"></button>
|
||||||
</row>
|
</row>
|
||||||
|
<row>
|
||||||
|
<button actionRef="photo-action"></button>
|
||||||
|
<button actionRef="video-action"></button>
|
||||||
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<button actionRef="url-action"></button>
|
<button actionRef="url-action"></button>
|
||||||
</row>
|
</row>
|
||||||
@@ -47,6 +58,10 @@
|
|||||||
<button actionRef="inline1-action"></button>
|
<button actionRef="inline1-action"></button>
|
||||||
<button actionRef="inline2-action"></button>
|
<button actionRef="inline2-action"></button>
|
||||||
</row>
|
</row>
|
||||||
|
<row>
|
||||||
|
<button actionRef="photo-action"></button>
|
||||||
|
<button actionRef="video-action"></button>
|
||||||
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<button actionRef="url-action"></button>
|
<button actionRef="url-action"></button>
|
||||||
</row>
|
</row>
|
||||||
@@ -59,17 +74,40 @@
|
|||||||
<button actionRef="inline1-action"></button>
|
<button actionRef="inline1-action"></button>
|
||||||
<button actionRef="inline2-action"></button>
|
<button actionRef="inline2-action"></button>
|
||||||
</row>
|
</row>
|
||||||
|
<row>
|
||||||
|
<button actionRef="photo-action"></button>
|
||||||
|
<button actionRef="video-action"></button>
|
||||||
|
</row>
|
||||||
<row>
|
<row>
|
||||||
<button actionRef="url-action"></button>
|
<button actionRef="url-action"></button>
|
||||||
</row>
|
</row>
|
||||||
</keyboard>
|
</keyboard>
|
||||||
</screen>
|
</screen>
|
||||||
|
<screen id="20006" name="inline-photo-screen" mediaRef="40002">
|
||||||
|
<text>My photo</text>
|
||||||
|
<keyboard>
|
||||||
|
<row>
|
||||||
|
<button actionRef="to-inline-back-action"></button>
|
||||||
|
</row>
|
||||||
|
</keyboard>
|
||||||
|
</screen>
|
||||||
|
<screen id="20007" name="inline-video-screen" mediaRef="40001">
|
||||||
|
<text>My Video</text>
|
||||||
|
<keyboard>
|
||||||
|
<row>
|
||||||
|
<button actionRef="to-inline-back-action"></button>
|
||||||
|
</row>
|
||||||
|
</keyboard>
|
||||||
|
</screen>
|
||||||
</screens>
|
</screens>
|
||||||
<commands>
|
<commands>
|
||||||
<command actionRef="start-action" screenRef="on-start-screen" id="30001" name="startCommand"/>
|
<command actionRef="start-action" screenRef="on-start-screen" id="30001" name="startCommand"/>
|
||||||
<command actionRef="help-action" screenRef="on-help-screen" id="30002" name="helpCommand"/>
|
<command actionRef="help-action" screenRef="on-help-screen" id="30002" name="helpCommand"/>
|
||||||
<command actionRef="to-inline-action" screenRef="inline-test-screen" id="30003" name="inlineTestCommand"/>
|
<command actionRef="to-inline-action" screenRef="inline-test-screen" id="30003" name="inlineTestCommand"/>
|
||||||
<command actionRef="inline1-action" screenRef="inline-test-1-screen" edit="true" id="30004" name="inlineTest1Command"/>
|
<command actionRef="to-inline-back-action" screenRef="inline-test-screen" replace="true" id="30004" name="inlineTestCommand"/>
|
||||||
<command actionRef="inline2-action" screenRef="inline-test-2-screen" edit="true" id="30005" name="inlineTest2Command"/>
|
<command actionRef="inline1-action" screenRef="inline-test-1-screen" edit="true" id="30005" name="inlineTest1Command"/>
|
||||||
|
<command actionRef="inline2-action" screenRef="inline-test-2-screen" edit="true" id="30006" name="inlineTest2Command"/>
|
||||||
|
<command actionRef="photo-action" screenRef="inline-photo-screen" replace="true" id="30005" name="photoCommand"/>
|
||||||
|
<command actionRef="video-action" screenRef="inline-video-screen" replace="true" id="30006" name="videoCommand"/>
|
||||||
</commands>
|
</commands>
|
||||||
</flow>
|
</flow>
|
||||||
@@ -7,10 +7,10 @@ import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
|||||||
import com.pengrad.telegrambot.model.request.KeyboardButton;
|
import com.pengrad.telegrambot.model.request.KeyboardButton;
|
||||||
import com.pengrad.telegrambot.model.request.ParseMode;
|
import com.pengrad.telegrambot.model.request.ParseMode;
|
||||||
import com.pengrad.telegrambot.model.request.ReplyKeyboardMarkup;
|
import com.pengrad.telegrambot.model.request.ReplyKeyboardMarkup;
|
||||||
|
import com.pengrad.telegrambot.request.AbstractSendRequest;
|
||||||
import com.pengrad.telegrambot.request.DeleteMessage;
|
import com.pengrad.telegrambot.request.DeleteMessage;
|
||||||
import com.pengrad.telegrambot.request.EditMessageText;
|
import com.pengrad.telegrambot.request.EditMessageText;
|
||||||
import com.pengrad.telegrambot.request.SendDocument;
|
import com.pengrad.telegrambot.request.SendDocument;
|
||||||
import com.pengrad.telegrambot.request.SendMessage;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import ru.penkrat.stbf.api.BotResponse;
|
import ru.penkrat.stbf.api.BotResponse;
|
||||||
@@ -28,10 +28,7 @@ public class BotResponseImpl implements BotResponse {
|
|||||||
public void send(Screen screen) {
|
public void send(Screen screen) {
|
||||||
log.debug("Send message: \n============\n{}\n============", screen.getText().trim());
|
log.debug("Send message: \n============\n{}\n============", screen.getText().trim());
|
||||||
|
|
||||||
SendMessage sendMessage = new SendMessage(chatId(), screen.getText().trim())
|
AbstractSendRequest<? extends AbstractSendRequest> sendMessage= SendMethodUtils.createFromScreen(chatId(), screen);
|
||||||
.parseMode(screen.getScreenProperties().isParseModeHtml() ? ParseMode.HTML : ParseMode.MarkdownV2)
|
|
||||||
.disableWebPagePreview(screen.getScreenProperties().isDisableWebPagePreview())
|
|
||||||
.disableNotification(screen.getScreenProperties().isDisableNotification());
|
|
||||||
|
|
||||||
if (screen.getKeyboard() instanceof KeyboardImpl) {
|
if (screen.getKeyboard() instanceof KeyboardImpl) {
|
||||||
KeyboardImpl kk = (KeyboardImpl) screen.getKeyboard();
|
KeyboardImpl kk = (KeyboardImpl) screen.getKeyboard();
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package ru.penkrat.stbf.impl.pengrad;
|
||||||
|
|
||||||
|
import com.pengrad.telegrambot.model.request.ParseMode;
|
||||||
|
import com.pengrad.telegrambot.request.AbstractSendRequest;
|
||||||
|
import com.pengrad.telegrambot.request.SendMessage;
|
||||||
|
import com.pengrad.telegrambot.request.SendPhoto;
|
||||||
|
import com.pengrad.telegrambot.request.SendVideo;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import ru.penkrat.stbf.api.Media;
|
||||||
|
import ru.penkrat.stbf.api.Screen;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
class SendMethodUtils {
|
||||||
|
|
||||||
|
public AbstractSendRequest<? extends AbstractSendRequest> createFromScreen(@NonNull Object chatId, @NonNull Screen screen) {
|
||||||
|
if (isMedia(screen)) {
|
||||||
|
final Media media = screen.getMedia();
|
||||||
|
switch (media.getMediaType()) {
|
||||||
|
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;
|
||||||
|
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 new SendMessage(chatId, screen.getText().trim())
|
||||||
|
.parseMode(screen.getScreenProperties().isParseModeHtml() ? ParseMode.HTML : ParseMode.MarkdownV2)
|
||||||
|
.disableWebPagePreview(screen.getScreenProperties().isDisableWebPagePreview())
|
||||||
|
.disableNotification(screen.getScreenProperties().isDisableNotification());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMedia(Screen screen) {
|
||||||
|
return screen.getMedia() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T, V> T apply(T target, Function<V, T> setter, V value) {
|
||||||
|
if (value != null) {
|
||||||
|
setter.apply(value);
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
<actions> </actions>
|
<actions> </actions>
|
||||||
<screens> </screens>
|
<screens> </screens>
|
||||||
<commands> </commands>
|
<commands> </commands>
|
||||||
|
<media> </media>
|
||||||
</flow>
|
</flow>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -51,6 +52,8 @@ Screen - то, что бот ответит пользователю, обычн
|
|||||||
</screen>
|
</screen>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`mediaRef` - ссылка на элемент медиа, который будет оправлен ботом
|
||||||
|
|
||||||
`text` - выводимый текст
|
`text` - выводимый текст
|
||||||
|
|
||||||
`keyboard` - описание клавиатуры
|
`keyboard` - описание клавиатуры
|
||||||
@@ -65,7 +68,7 @@ Screen - то, что бот ответит пользователю, обычн
|
|||||||
|
|
||||||
#### Button
|
#### Button
|
||||||
|
|
||||||
`if` - видомость кнопки, значение `true`, `false` или имя метода из контекста экрана (для программной обработки)
|
`if` - видимость кнопки, значение `true`, `false` или имя метода из контекста экрана (для программной обработки)
|
||||||
|
|
||||||
`actionRef` - `id` или `name` action, описанный в соответсвующей секции
|
`actionRef` - `id` или `name` action, описанный в соответсвующей секции
|
||||||
|
|
||||||
@@ -89,4 +92,21 @@ Screen - то, что бот ответит пользователю, обычн
|
|||||||
```
|
```
|
||||||
|
|
||||||
`actionRef` - ссылка на action, может использоваться id или name
|
`actionRef` - ссылка на action, может использоваться id или name
|
||||||
|
|
||||||
`screenRef` - ссылка на screen, может использоваться id или name
|
`screenRef` - ссылка на screen, может использоваться id или name
|
||||||
|
|
||||||
|
`edit` = `true|false`- исходное сообщение будет отредактировано (актуально для callback)
|
||||||
|
|
||||||
|
`replace` = `true|false`- исходное сообщение будет удалено, и отправлено новое (актуально для callback,
|
||||||
|
если меняется тип сообщения т.е. сообщение с фото, видео должно быть заменено на текстовое и наоборот)
|
||||||
|
|
||||||
|
### Media
|
||||||
|
|
||||||
|
Описывает медиа-ресурсы, доступные для отправки ботом
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<media>
|
||||||
|
<video id="40001" url="https://example.com/video.mp4"/>
|
||||||
|
<photo id="40002" url="https://example.com//photo.png"/>
|
||||||
|
</media>
|
||||||
|
```
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package ru.penkrat.stbf.templates;
|
||||||
|
|
||||||
|
import ru.penkrat.stbf.api.Media;
|
||||||
|
|
||||||
|
public interface MediaResolver {
|
||||||
|
|
||||||
|
Media getMedia(String name);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -24,4 +24,7 @@ public class CommandItem extends NamedItem {
|
|||||||
@JacksonXmlProperty(isAttribute = true)
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
private boolean edit;
|
private boolean edit;
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private boolean replace;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,19 +62,22 @@ public class FlowCommandResolverDelegate implements CommandResolver {
|
|||||||
screenFactory = screenResolver.getScreenFactory(commandItem.getScreenRef());
|
screenFactory = screenResolver.getScreenFactory(commandItem.getScreenRef());
|
||||||
}
|
}
|
||||||
if (actionMatcher != null && screen != null) {
|
if (actionMatcher != null && screen != null) {
|
||||||
return simpleCommand(actionMatcher, screen, commandItem.isEdit(), commandItem.getId(), commandItem.getName());
|
return simpleCommand(actionMatcher, screen, commandItem.isEdit(), commandItem.isReplace(), commandItem.getId(), commandItem.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Command simpleCommand(RequestMatcher matcher, Screen screen, boolean edit, String id, String name) {
|
private Command simpleCommand(RequestMatcher matcher, Screen screen, boolean edit, boolean replace, String id, String name) {
|
||||||
return new Command() {
|
return new Command() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(BotRequest botRequest, BotResponse botResponse, CommandChain chain) {
|
public void process(BotRequest botRequest, BotResponse botResponse, CommandChain chain) {
|
||||||
if (matcher.match(botRequest)) {
|
if (matcher.match(botRequest)) {
|
||||||
if (edit) {
|
if (replace) {
|
||||||
|
botResponse.deleteMessage();
|
||||||
|
}
|
||||||
|
if (edit && !replace) {
|
||||||
botResponse.edit(screen);
|
botResponse.edit(screen);
|
||||||
} else {
|
} else {
|
||||||
botResponse.send(screen);
|
botResponse.send(screen);
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
import ru.penkrat.stbf.api.Media;
|
||||||
|
import ru.penkrat.stbf.api.MediaType;
|
||||||
|
import ru.penkrat.stbf.templates.MediaResolver;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class FlowMediaResolverDelegate implements MediaResolver {
|
||||||
|
|
||||||
|
private final NamedItemResolver<MediaItem> videosResolver;
|
||||||
|
private final NamedItemResolver<MediaItem> photosResolver;
|
||||||
|
|
||||||
|
FlowMediaResolverDelegate(FlowRoot src) {
|
||||||
|
videosResolver = new NamedItemResolver<>(Traversal.traverse(src, FlowMediaResolverDelegate::videos));
|
||||||
|
photosResolver = new NamedItemResolver<>(Traversal.traverse(src, FlowMediaResolverDelegate::photos));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Media getMedia(String name) {
|
||||||
|
Optional<Media> media = videosResolver.getOpt(name)
|
||||||
|
.map(FlowMediaResolverDelegate::toVideo);
|
||||||
|
if(media.isPresent()){
|
||||||
|
return media.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
media = photosResolver.getOpt(name)
|
||||||
|
.map(FlowMediaResolverDelegate::toPhoto);
|
||||||
|
if(media.isPresent()){
|
||||||
|
return media.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalStateException("MediaElement not found by 'id' or 'name' " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Media toVideo(MediaItem item) {
|
||||||
|
return Media.builder()
|
||||||
|
.mediaType(MediaType.VIDEO)
|
||||||
|
.url(item.getUrl())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Media toPhoto(MediaItem item) {
|
||||||
|
return Media.builder()
|
||||||
|
.mediaType(MediaType.PHOTO)
|
||||||
|
.url(item.getUrl())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection<MediaItem> videos(FlowRoot root) {
|
||||||
|
return root.getMedia().getVideos();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection<MediaItem> photos(FlowRoot root) {
|
||||||
|
return root.getMedia().getPhotos();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,10 @@ class FlowRoot {
|
|||||||
@JsonProperty("command")
|
@JsonProperty("command")
|
||||||
private List<CommandItem> commands = new ArrayList<>();
|
private List<CommandItem> commands = new ArrayList<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@JsonProperty("media")
|
||||||
|
private MediaRoot media = new MediaRoot();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private transient List<FlowRoot> included = new ArrayList<>();
|
private transient List<FlowRoot> included = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ import lombok.val;
|
|||||||
import ru.penkrat.stbf.api.Action;
|
import ru.penkrat.stbf.api.Action;
|
||||||
import ru.penkrat.stbf.api.Keyboard;
|
import ru.penkrat.stbf.api.Keyboard;
|
||||||
import ru.penkrat.stbf.api.KeyboardBuilder;
|
import ru.penkrat.stbf.api.KeyboardBuilder;
|
||||||
|
import ru.penkrat.stbf.api.Media;
|
||||||
import ru.penkrat.stbf.api.Screen;
|
import ru.penkrat.stbf.api.Screen;
|
||||||
|
import ru.penkrat.stbf.common.screen.MediaScreen;
|
||||||
import ru.penkrat.stbf.common.screen.TextScreen;
|
import ru.penkrat.stbf.common.screen.TextScreen;
|
||||||
import ru.penkrat.stbf.templates.ActionResolver;
|
import ru.penkrat.stbf.templates.ActionResolver;
|
||||||
import ru.penkrat.stbf.templates.KeyboardProvider;
|
import ru.penkrat.stbf.templates.KeyboardProvider;
|
||||||
|
import ru.penkrat.stbf.templates.MediaResolver;
|
||||||
import ru.penkrat.stbf.templates.ScreenResolver;
|
import ru.penkrat.stbf.templates.ScreenResolver;
|
||||||
import ru.penkrat.stbf.templates.TemplateRenderer;
|
import ru.penkrat.stbf.templates.TemplateRenderer;
|
||||||
import ru.penkrat.stbf.templates.utils.ReflectionUtils;
|
import ru.penkrat.stbf.templates.utils.ReflectionUtils;
|
||||||
@@ -25,11 +28,14 @@ class FlowScreenResolverDelegate implements ScreenResolver {
|
|||||||
|
|
||||||
private final ActionResolver actionResolver;
|
private final ActionResolver actionResolver;
|
||||||
|
|
||||||
|
private final MediaResolver mediaResolver;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private TemplateRenderer templateRenderer = new NoopTemplateRenderer();
|
private TemplateRenderer templateRenderer = new NoopTemplateRenderer();
|
||||||
|
|
||||||
FlowScreenResolverDelegate(FlowRoot src, ActionResolver actionResolver) {
|
FlowScreenResolverDelegate(FlowRoot src, ActionResolver actionResolver, MediaResolver mediaResolver) {
|
||||||
this.actionResolver = actionResolver;
|
this.actionResolver = actionResolver;
|
||||||
|
this.mediaResolver = mediaResolver;
|
||||||
resolver = new NamedItemResolver<>(Traversal.traverse(src, FlowRoot::getScreens));
|
resolver = new NamedItemResolver<>(Traversal.traverse(src, FlowRoot::getScreens));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +43,11 @@ class FlowScreenResolverDelegate implements ScreenResolver {
|
|||||||
public Screen getScreen(String name) {
|
public Screen getScreen(String name) {
|
||||||
ScreenItem item = resolver.get(name);
|
ScreenItem item = resolver.get(name);
|
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(item.getMediaRef())) {
|
||||||
|
final Media media = mediaResolver.getMedia(item.getMediaRef());
|
||||||
|
return new MediaScreen(item.getText(), media, buildKeyboard(item.getKeyboard(), null));
|
||||||
|
}
|
||||||
|
|
||||||
return new TextScreen(item.getText(), buildKeyboard(item.getKeyboard(), null));
|
return new TextScreen(item.getText(), buildKeyboard(item.getKeyboard(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +55,12 @@ class FlowScreenResolverDelegate implements ScreenResolver {
|
|||||||
public Screen getScreen(String name, Object context) {
|
public Screen getScreen(String name, Object context) {
|
||||||
ScreenItem item = resolver.get(name);
|
ScreenItem item = resolver.get(name);
|
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(item.getMediaRef())) {
|
||||||
|
final Media media = mediaResolver.getMedia(item.getMediaRef());
|
||||||
|
return new MediaScreen(templateRenderer.render(item.getText(), context), media,
|
||||||
|
resolveKeyboard(item.getKeyboard(), context));
|
||||||
|
}
|
||||||
|
|
||||||
return new TextScreen(templateRenderer.render(item.getText(), context),
|
return new TextScreen(templateRenderer.render(item.getText(), context),
|
||||||
resolveKeyboard(item.getKeyboard(), context));
|
resolveKeyboard(item.getKeyboard(), context));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class MediaItem extends NamedItem {
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MediaRoot {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@JacksonXmlElementWrapper(useWrapping = false)
|
||||||
|
@JsonProperty("video")
|
||||||
|
private List<MediaItem> videos = new ArrayList<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@JacksonXmlElementWrapper(useWrapping = false)
|
||||||
|
@JsonProperty("photo")
|
||||||
|
private List<MediaItem> photos = new ArrayList<>();
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import ru.penkrat.stbf.templates.utils.StringUtils;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
class NamedItemResolver<T extends NamedItem> {
|
class NamedItemResolver<T extends NamedItem> {
|
||||||
@@ -39,4 +40,22 @@ class NamedItemResolver<T extends NamedItem> {
|
|||||||
throw new IllegalStateException("Element not found by 'id' or 'name' " + key);
|
throw new IllegalStateException("Element not found by 'id' or 'name' " + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<T> getOpt(String key) {
|
||||||
|
List<T> list = byId.get(key);
|
||||||
|
if (list != null) {
|
||||||
|
if (list.size() > 1) {
|
||||||
|
throw new IllegalStateException("Non unique 'id' " + key);
|
||||||
|
}
|
||||||
|
return Optional.of(list.get(0));
|
||||||
|
}
|
||||||
|
list = byName.get(key);
|
||||||
|
if (list != null) {
|
||||||
|
if (list.size() > 1) {
|
||||||
|
throw new IllegalStateException("Non unique 'name' " + key);
|
||||||
|
}
|
||||||
|
return Optional.of(list.get(0));
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package ru.penkrat.stbf.templates.xml;
|
package ru.penkrat.stbf.templates.xml;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@@ -9,6 +10,9 @@ class ScreenItem extends NamedItem {
|
|||||||
|
|
||||||
private String text;
|
private String text;
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true)
|
||||||
|
private String mediaRef;
|
||||||
|
|
||||||
private KeyboardWrapper keyboard = new KeyboardWrapper();
|
private KeyboardWrapper keyboard = new KeyboardWrapper();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,18 +18,21 @@ public class XmlFlowResolver implements FlowResolver {
|
|||||||
private final FlowActionResolverDelegate actionDelegate;
|
private final FlowActionResolverDelegate actionDelegate;
|
||||||
private final FlowScreenResolverDelegate screenDelegate;
|
private final FlowScreenResolverDelegate screenDelegate;
|
||||||
private final FlowCommandResolverDelegate commandResolver;
|
private final FlowCommandResolverDelegate commandResolver;
|
||||||
|
private final FlowMediaResolverDelegate mediaResolver;
|
||||||
|
|
||||||
public XmlFlowResolver(String filename) {
|
public XmlFlowResolver(String filename) {
|
||||||
FlowRoot flow = reader.read(filename);
|
FlowRoot flow = reader.read(filename);
|
||||||
actionDelegate = new FlowActionResolverDelegate(flow);
|
actionDelegate = new FlowActionResolverDelegate(flow);
|
||||||
screenDelegate = new FlowScreenResolverDelegate(flow, this);
|
mediaResolver = new FlowMediaResolverDelegate(flow);
|
||||||
|
screenDelegate = new FlowScreenResolverDelegate(flow, this, mediaResolver);
|
||||||
commandResolver = new FlowCommandResolverDelegate(flow, actionDelegate, screenDelegate);
|
commandResolver = new FlowCommandResolverDelegate(flow, actionDelegate, screenDelegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmlFlowResolver(InputStream is) {
|
public XmlFlowResolver(InputStream is) {
|
||||||
FlowRoot flow = reader.read(is);
|
FlowRoot flow = reader.read(is);
|
||||||
actionDelegate = new FlowActionResolverDelegate(flow);
|
actionDelegate = new FlowActionResolverDelegate(flow);
|
||||||
screenDelegate = new FlowScreenResolverDelegate(flow, this);
|
mediaResolver = new FlowMediaResolverDelegate(flow);
|
||||||
|
screenDelegate = new FlowScreenResolverDelegate(flow, this, mediaResolver);
|
||||||
commandResolver = new FlowCommandResolverDelegate(flow, actionDelegate, screenDelegate);
|
commandResolver = new FlowCommandResolverDelegate(flow, actionDelegate, screenDelegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ru.penkrat.stbf.templates.xml;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
import lombok.val;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import ru.penkrat.stbf.api.Command;
|
import ru.penkrat.stbf.api.Command;
|
||||||
@@ -42,6 +43,7 @@ public class XmlWriterTest {
|
|||||||
item2.setId("2");
|
item2.setId("2");
|
||||||
item2.setName("screen-2");
|
item2.setName("screen-2");
|
||||||
item2.setText("Hello, {{ name }}");
|
item2.setText("Hello, {{ name }}");
|
||||||
|
item2.setMediaRef("12");
|
||||||
|
|
||||||
item2.getKeyboard().setFactoryMethod("getKeyboard");
|
item2.getKeyboard().setFactoryMethod("getKeyboard");
|
||||||
|
|
||||||
@@ -58,6 +60,16 @@ public class XmlWriterTest {
|
|||||||
commandItem.setClazz(Command.class.getCanonicalName());
|
commandItem.setClazz(Command.class.getCanonicalName());
|
||||||
root.getCommands().add(commandItem);
|
root.getCommands().add(commandItem);
|
||||||
|
|
||||||
|
|
||||||
|
MediaItem video1 = new MediaItem();
|
||||||
|
video1.setId("11");
|
||||||
|
video1.setUrl("https://example.com/test.mpg");
|
||||||
|
MediaItem photo1 = new MediaItem();
|
||||||
|
photo1.setId("12");
|
||||||
|
photo1.setUrl("https://example.com/test.jpg");
|
||||||
|
root.getMedia().getVideos().add(video1);
|
||||||
|
root.getMedia().getPhotos().add(photo1);
|
||||||
|
|
||||||
String xml = mapper.writeValueAsString(root);
|
String xml = mapper.writeValueAsString(root);
|
||||||
|
|
||||||
System.out.println(xml);
|
System.out.println(xml);
|
||||||
|
|||||||
Reference in New Issue
Block a user