diff --git a/bundles/org.openhab.binding.telegram/README.md b/bundles/org.openhab.binding.telegram/README.md index 298e9540c18ee..9e27fd5d1eb73 100644 --- a/bundles/org.openhab.binding.telegram/README.md +++ b/bundles/org.openhab.binding.telegram/README.md @@ -193,18 +193,21 @@ Each of the actions returns true on success or false on failure. These actions will send a message to all chat ids configured for this bot. -| Action | Description | -|--------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------| -| sendTelegram(String message) | Sends a message. | -| sendTelegram(String format, Object... args) | Sends a formatted message (See for more information) | -| sendTelegramTo(Long[] chatIds, String message, Integer replyMessageId,Boolean silent, Integer messageThreadId, Object... args) | Sends a message with various options | -| sendTelegramQuery(String message, String replyId, String... buttons) | Sends a question to the user that can be answered via the defined buttons. The replyId can be freely choosen and is sent back with the answer. Then, the id is required to identify what question has been answered (e.g. in case of multiple open questions). The final result looks like this: ![Telegram Inline Keyboard](doc/queryExample.png) | -| sendTelegramAnswer(String replyId, String message) | Sends a message after the user has answered a question. You should _always_ call this method after you received an answer. It will remove buttons from the specific question and will also stop the progress bar displayed at the client side. If no message is necessary, just pass `null` here. | -| deleteTelegramQuery(String replyId) | Deletes a question in the chat. The replyId must be the same as used for the corresponding sendTelegramQuery() action. | -| sendTelegramPhoto(String photoURL, String caption) | Sends a picture. Can be one of the URL formats, see the Note below, or a base64 encoded image (simple base64 data or data URI scheme). | -| sendTelegramPhoto(String photoURL, String caption, String username, String password) | Sends a picture which is downloaded from a username/password protected http/https address. | -| sendTelegramAnimation(String animationURL, String caption) | Send animation files either GIF or H.264/MPEG-4 AVC video without sound. | -| sendTelegramVideo(String videoURL, String caption) | Send MP4 video files up to 50MB. | +| Action | Description | +|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| sendTelegram(String message) | Sends a message. | +| sendTelegram(String format, Object... args) | Sends a formatted message (See for more information) | +| sendTelegramTo(Long[] chatIds, String message, Integer replyMessageId,Boolean silent, Integer messageThreadId, Object... args) | Sends a message with various options | +| sendTelegramQuery(String message, String replyId, String... buttons) | Sends a question to the user that can be answered via the defined buttons. The replyId can be freely choosen and is sent back with the answer. Then, the id is required to identify what question has been answered (e.g. in case of multiple open questions). The final result looks like this: ![Telegram Inline Keyboard](doc/queryExample.png) | +| sendTelegramAnswer(String replyId, String message) | Sends a message after the user has answered a question. You should _always_ call this method after you received an answer. It will remove buttons from the specific question and will also stop the progress bar displayed at the client side. If no message is necessary, just pass `null` here. | +| deleteTelegramQuery(String replyId) | Deletes a question in the chat. The replyId must be the same as used for the corresponding sendTelegramQuery() action. | +| sendTelegramPhoto(String photoURL, String caption) | Sends a picture. Can be one of the URL formats, see the Note below, or a base64 encoded image (simple base64 data or data URI scheme). | +| sendTelegramPhoto(String photoURL, String caption, String username, String password) | Sends a picture which is downloaded from a username/password protected http/https address. | +| sendTelegramAnimation(String animationURL, String caption) | Send animation files either GIF or H.264/MPEG-4 AVC video without sound. | +| sendTelegramVideo(String videoURL, String caption) | Send MP4 video files up to 50MB. | +| `sendTelegramMediaGroup(List mediaUrls, List mediaTypes)` | Sends a group of photos, videos, or other media as an album. `mediaTypes` must contain corresponding media types (`photo`, `video`, `animation`, `audio`, `document`) for each URL. Must contain 2-10 items. | +| `sendTelegramMediaGroup(Long chatId, List mediaUrls, List mediaTypes)` | Sends a media group to a specific chat. See above for details. | +| `sendTelegramMediaGroup(Long chatId, List mediaUrls, List mediaTypes, Integer replyToMessageId, Boolean disableNotification, Integer messageThreadId)` | Sends a media group with optional parameters: `replyToMessageId` (reply to a message), `disableNotification` (send silently), `messageThreadId` (for topics in supergroups). | **Note:** In actions that require a file URL, the following formats are acceptable: diff --git a/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/action/TelegramActions.java b/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/action/TelegramActions.java index 1adac9939ffdf..d0cc022de0bf7 100644 --- a/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/action/TelegramActions.java +++ b/bundles/org.openhab.binding.telegram/src/main/java/org/openhab/binding/telegram/internal/action/TelegramActions.java @@ -52,10 +52,17 @@ import com.pengrad.telegrambot.model.request.InlineKeyboardButton; import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; +import com.pengrad.telegrambot.model.request.InputMedia; +import com.pengrad.telegrambot.model.request.InputMediaAnimation; +import com.pengrad.telegrambot.model.request.InputMediaAudio; +import com.pengrad.telegrambot.model.request.InputMediaDocument; +import com.pengrad.telegrambot.model.request.InputMediaPhoto; +import com.pengrad.telegrambot.model.request.InputMediaVideo; import com.pengrad.telegrambot.request.AnswerCallbackQuery; import com.pengrad.telegrambot.request.DeleteMessage; import com.pengrad.telegrambot.request.EditMessageReplyMarkup; import com.pengrad.telegrambot.request.SendAnimation; +import com.pengrad.telegrambot.request.SendMediaGroup; import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendPhoto; import com.pengrad.telegrambot.request.SendVideo; @@ -530,6 +537,104 @@ private boolean sendTelegramGeneral(@Nullable Long chatId, @Nullable String mess return sendTelegramPhoto(photoURL, caption, null, null); } + @RuleAction(label = "send a media group", description = "Send a Telegram media group using the Telegram API.") + public @ActionOutput(label = "Success", type = "java.lang.Boolean") boolean sendTelegramMediaGroup( + @ActionInput(name = "chatId") @Nullable Long chatId, + @ActionInput(name = "mediaUrls") @Nullable List mediaUrls, + @ActionInput(name = "mediaTypes") @Nullable List mediaTypes) { + return sendTelegramMediaGroup(chatId, mediaUrls, mediaTypes, null, null, null); + } + + @RuleAction(label = "send a media group", description = "Send a Telegram media group using the Telegram API.") + public @ActionOutput(label = "Success", type = "java.lang.Boolean") boolean sendTelegramMediaGroup( + @ActionInput(name = "mediaUrls") @Nullable List mediaUrls, + @ActionInput(name = "mediaTypes") @Nullable List mediaTypes) { + return sendTelegramMediaGroup(null, mediaUrls, mediaTypes, null, null, null); + } + + @RuleAction(label = "send a media group", description = "Send a Telegram media group using the Telegram API.") + public @ActionOutput(label = "Success", type = "java.lang.Boolean") boolean sendTelegramMediaGroup( + @ActionInput(name = "chatId") @Nullable Long chatId, + @ActionInput(name = "mediaUrls") @Nullable List mediaUrls, + @ActionInput(name = "mediaTypes") @Nullable List mediaTypes, + @ActionInput(name = "replyToMessageId") @Nullable Integer replyToMessageId, + @ActionInput(name = "disableNotification") @Nullable Boolean disableNotification, + @ActionInput(name = "messageThreadId") @Nullable Integer messageThreadId) { + + TelegramHandler localHandler = handler; + if (localHandler == null) { + logger.warn("TelegramActions: Action service ThingHandler is null"); + return false; + } + + if (mediaUrls == null || mediaUrls.isEmpty()) { + logger.warn("mediaUrls cannot be null or empty"); + return false; + } + + if (mediaTypes == null || mediaTypes.size() != mediaUrls.size()) { + logger.warn("mediaTypes must have the same size as mediaUrls"); + return false; + } + + List chatIdentifiers = (chatId != null) ? List.of(chatId) : localHandler.getReceiverChatIds(); + + boolean successful = true; + for (Long chat : chatIdentifiers) { + InputMedia[] mediaArray = new InputMedia[mediaUrls.size()]; + + for (int i = 0; i < mediaUrls.size(); i++) { + String url = mediaUrls.get(i); + String type = mediaTypes.get(i).toLowerCase(); + + try { + InputMedia media = switch (type) { + case "photo" -> new InputMediaPhoto(url); + case "video" -> new InputMediaVideo(url); + case "animation" -> new InputMediaAnimation(url); + case "audio" -> new InputMediaAudio(url); + case "document" -> new InputMediaDocument(url); + default -> { + logger.warn("Unknown media type: {}", type); + yield null; + } + }; + + if (media == null) { + successful = false; + break; + } + + mediaArray[i] = media; + } catch (IllegalArgumentException e) { + logger.warn("Failed to create media object for URL {}: {}", url, e.getMessage()); + successful = false; + break; + } + } + + if (!successful) { + continue; + } + + SendMediaGroup request = new SendMediaGroup(chat, mediaArray); + + if (replyToMessageId != null) { + request.replyToMessageId(replyToMessageId); + } + if (disableNotification != null) { + request.disableNotification(disableNotification); + } + if (messageThreadId != null) { + request.messageThreadId(messageThreadId); + } + + successful &= evaluateResponse(localHandler.execute(request)); + } + + return successful; + } + @RuleAction(label = "send animation", description = "Send an Animation using the Telegram API.") public @ActionOutput(label = "Success", type = "java.lang.Boolean") boolean sendTelegramAnimation( @ActionInput(name = "animationURL") @Nullable String animationURL, @@ -783,6 +888,23 @@ public static boolean sendTelegramAnswer(ThingActions actions, @Nullable Long ch return ((TelegramActions) actions).sendTelegramAnswer(chatId, replyId, message); } + public static boolean sendTelegramMediaGroup(ThingActions actions, @Nullable Long chatId, + @Nullable List mediaUrls, @Nullable List mediaTypes) { + return ((TelegramActions) actions).sendTelegramMediaGroup(chatId, mediaUrls, mediaTypes); + } + + public static boolean sendTelegramMediaGroup(ThingActions actions, @Nullable List mediaUrls, + @Nullable List mediaTypes) { + return ((TelegramActions) actions).sendTelegramMediaGroup(mediaUrls, mediaTypes); + } + + public static boolean sendTelegramMediaGroup(ThingActions actions, @Nullable Long chatId, + @Nullable List mediaUrls, @Nullable List mediaTypes, @Nullable Integer replyToMessageId, + @Nullable Boolean disableNotification, @Nullable Integer messageThreadId) { + return ((TelegramActions) actions).sendTelegramMediaGroup(chatId, mediaUrls, mediaTypes, replyToMessageId, + disableNotification, messageThreadId); + } + public static boolean sendTelegramAnswer(ThingActions actions, @Nullable String chatId, @Nullable String replyId, @Nullable String message) { if (actions instanceof TelegramActions telegramActions) {