]> git.basschouten.com Git - openhab-addons.git/blob
f25c42eb8fbe0b268638a0dadeadf006daba4260
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.telegram.internal.action;
14
15 import static org.openhab.binding.telegram.internal.TelegramBindingConstants.PHOTO_EXTENSIONS;
16
17 import java.io.ByteArrayInputStream;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.net.MalformedURLException;
21 import java.net.URI;
22 import java.net.URL;
23 import java.nio.charset.StandardCharsets;
24 import java.nio.file.Path;
25 import java.util.Base64;
26 import java.util.concurrent.ExecutionException;
27 import java.util.concurrent.TimeUnit;
28
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.eclipse.jetty.client.HttpClient;
32 import org.eclipse.jetty.client.api.Authentication;
33 import org.eclipse.jetty.client.api.AuthenticationStore;
34 import org.eclipse.jetty.client.api.ContentResponse;
35 import org.eclipse.jetty.client.api.Request;
36 import org.eclipse.jetty.client.util.FutureResponseListener;
37 import org.eclipse.jetty.http.HttpHeader;
38 import org.eclipse.jetty.http.HttpMethod;
39 import org.openhab.binding.telegram.internal.TelegramHandler;
40 import org.openhab.core.automation.annotation.ActionInput;
41 import org.openhab.core.automation.annotation.RuleAction;
42 import org.openhab.core.thing.binding.ThingActions;
43 import org.openhab.core.thing.binding.ThingActionsScope;
44 import org.openhab.core.thing.binding.ThingHandler;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
49 import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
50 import com.pengrad.telegrambot.request.AnswerCallbackQuery;
51 import com.pengrad.telegrambot.request.EditMessageReplyMarkup;
52 import com.pengrad.telegrambot.request.SendAnimation;
53 import com.pengrad.telegrambot.request.SendMessage;
54 import com.pengrad.telegrambot.request.SendPhoto;
55 import com.pengrad.telegrambot.request.SendVideo;
56 import com.pengrad.telegrambot.response.BaseResponse;
57 import com.pengrad.telegrambot.response.SendResponse;
58
59 /**
60  * Provides the actions for the Telegram API.
61  *
62  * @author Alexander Krasnogolowy - Initial contribution
63  */
64 @ThingActionsScope(name = "telegram")
65 @NonNullByDefault
66 public class TelegramActions implements ThingActions {
67     private final Logger logger = LoggerFactory.getLogger(TelegramActions.class);
68     private @Nullable TelegramHandler handler;
69
70     private boolean evaluateResponse(@Nullable BaseResponse response) {
71         if (response != null && !response.isOk()) {
72             logger.warn("Failed to send telegram message: {}", response.description());
73             return false;
74         }
75         return true;
76     }
77
78     private static class BasicResult implements Authentication.Result {
79
80         private final HttpHeader header;
81         private final URI uri;
82         private final String value;
83
84         public BasicResult(HttpHeader header, URI uri, String value) {
85             this.header = header;
86             this.uri = uri;
87             this.value = value;
88         }
89
90         @Override
91         public URI getURI() {
92             return this.uri;
93         }
94
95         @Override
96         public void apply(@Nullable Request request) {
97             if (request != null) {
98                 request.header(this.header, this.value);
99             }
100         }
101
102         @Override
103         public String toString() {
104             return String.format("Basic authentication result for %s", this.uri);
105         }
106     }
107
108     @RuleAction(label = "send an answer", description = "Send a Telegram answer using the Telegram API.")
109     public boolean sendTelegramAnswer(@ActionInput(name = "chatId") @Nullable Long chatId,
110             @ActionInput(name = "replyId") @Nullable String replyId,
111             @ActionInput(name = "message") @Nullable String message) {
112         if (replyId == null) {
113             logger.warn("ReplyId not defined; action skipped.");
114             return false;
115         }
116         if (chatId == null) {
117             logger.warn("chatId not defined; action skipped.");
118             return false;
119         }
120         TelegramHandler localHandler = handler;
121         if (localHandler != null) {
122             String callbackId = localHandler.getCallbackId(chatId, replyId);
123             if (callbackId != null) {
124                 AnswerCallbackQuery answerCallbackQuery = new AnswerCallbackQuery(
125                         localHandler.getCallbackId(chatId, replyId));
126                 logger.debug("AnswerCallbackQuery for chatId {} and replyId {} is the callbackId {}", chatId, replyId,
127                         localHandler.getCallbackId(chatId, replyId));
128                 // we could directly set the text here, but this
129                 // doesn't result in a real message only in a
130                 // little popup or in an alert, so the only purpose
131                 // is to stop the progress bar on client side
132                 if (!evaluateResponse(localHandler.execute(answerCallbackQuery))) {
133                     return false;
134                 }
135             }
136             Integer messageId = localHandler.removeMessageId(chatId, replyId);
137             logger.debug("remove messageId {} for chatId {} and replyId {}", messageId, chatId, replyId);
138
139             EditMessageReplyMarkup editReplyMarkup = new EditMessageReplyMarkup(chatId, messageId.intValue())
140                     .replyMarkup(new InlineKeyboardMarkup(new InlineKeyboardButton[0]));// remove reply markup from
141                                                                                         // old message
142             if (!evaluateResponse(localHandler.execute(editReplyMarkup))) {
143                 return false;
144             }
145             return message != null ? sendTelegram(chatId, message) : true;
146         }
147         return false;
148     }
149
150     @RuleAction(label = "send an answer", description = "Send a Telegram answer using the Telegram API.")
151     public boolean sendTelegramAnswer(@ActionInput(name = "replyId") @Nullable String replyId,
152             @ActionInput(name = "message") @Nullable String message) {
153         TelegramHandler localHandler = handler;
154         if (localHandler != null) {
155             for (Long chatId : localHandler.getReceiverChatIds()) {
156                 if (!sendTelegramAnswer(chatId, replyId, message)) {
157                     return false;
158                 }
159             }
160         }
161         return true;
162     }
163
164     @RuleAction(label = "send a message", description = "Send a Telegram message using the Telegram API.")
165     public boolean sendTelegram(@ActionInput(name = "chatId") @Nullable Long chatId,
166             @ActionInput(name = "message") @Nullable String message) {
167         return sendTelegramGeneral(chatId, message, (String) null);
168     }
169
170     @RuleAction(label = "send a message", description = "Send a Telegram message using the Telegram API.")
171     public boolean sendTelegram(@ActionInput(name = "message") @Nullable String message) {
172         TelegramHandler localHandler = handler;
173         if (localHandler != null) {
174             for (Long chatId : localHandler.getReceiverChatIds()) {
175                 if (!sendTelegram(chatId, message)) {
176                     return false;
177                 }
178             }
179         }
180         return true;
181     }
182
183     @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
184     public boolean sendTelegramQuery(@ActionInput(name = "chatId") @Nullable Long chatId,
185             @ActionInput(name = "message") @Nullable String message,
186             @ActionInput(name = "replyId") @Nullable String replyId,
187             @ActionInput(name = "buttons") @Nullable String... buttons) {
188         return sendTelegramGeneral(chatId, message, replyId, buttons);
189     }
190
191     @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
192     public boolean sendTelegramQuery(@ActionInput(name = "message") @Nullable String message,
193             @ActionInput(name = "replyId") @Nullable String replyId,
194             @ActionInput(name = "buttons") @Nullable String... buttons) {
195         TelegramHandler localHandler = handler;
196         if (localHandler != null) {
197             for (Long chatId : localHandler.getReceiverChatIds()) {
198                 if (!sendTelegramQuery(chatId, message, replyId, buttons)) {
199                     return false;
200                 }
201             }
202         }
203         return true;
204     }
205
206     private boolean sendTelegramGeneral(@ActionInput(name = "chatId") @Nullable Long chatId, @Nullable String message,
207             @Nullable String replyId, @Nullable String... buttons) {
208         if (message == null) {
209             logger.warn("Message not defined; action skipped.");
210             return false;
211         }
212         if (chatId == null) {
213             logger.warn("chatId not defined; action skipped.");
214             return false;
215         }
216         TelegramHandler localHandler = handler;
217         if (localHandler != null) {
218             SendMessage sendMessage = new SendMessage(chatId, message);
219             if (localHandler.getParseMode() != null) {
220                 sendMessage.parseMode(localHandler.getParseMode());
221             }
222             if (replyId != null) {
223                 if (!replyId.contains(" ")) {
224                     if (buttons.length > 0) {
225                         InlineKeyboardButton[][] keyboard2D = new InlineKeyboardButton[1][];
226                         InlineKeyboardButton[] keyboard = new InlineKeyboardButton[buttons.length];
227                         keyboard2D[0] = keyboard;
228                         for (int i = 0; i < buttons.length; i++) {
229                             keyboard[i] = new InlineKeyboardButton(buttons[i]).callbackData(replyId + " " + buttons[i]);
230                         }
231                         InlineKeyboardMarkup keyBoardMarkup = new InlineKeyboardMarkup(keyboard2D);
232                         sendMessage.replyMarkup(keyBoardMarkup);
233                     } else {
234                         logger.warn(
235                                 "The replyId {} for message {} is given, but no buttons are defined. ReplyMarkup will be ignored.",
236                                 replyId, message);
237                     }
238                 } else {
239                     logger.warn("replyId {} must not contain spaces. ReplyMarkup will be ignored.", replyId);
240                 }
241             }
242             SendResponse retMessage = localHandler.execute(sendMessage);
243             if (!evaluateResponse(retMessage)) {
244                 return false;
245             }
246             if (replyId != null && retMessage != null) {
247                 logger.debug("Adding chatId {}, replyId {} and messageId {}", chatId, replyId,
248                         retMessage.message().messageId());
249                 localHandler.addMessageId(chatId, replyId, retMessage.message().messageId());
250             }
251             return true;
252         }
253         return false;
254     }
255
256     @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
257     public boolean sendTelegram(@ActionInput(name = "chatId") @Nullable Long chatId,
258             @ActionInput(name = "message") @Nullable String message,
259             @ActionInput(name = "args") @Nullable Object... args) {
260         if (message == null) {
261             return false;
262         }
263         return sendTelegram(chatId, String.format(message, args));
264     }
265
266     @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
267     public boolean sendTelegram(@ActionInput(name = "message") @Nullable String message,
268             @ActionInput(name = "args") @Nullable Object... args) {
269         TelegramHandler localHandler = handler;
270         if (localHandler != null) {
271             for (Long chatId : localHandler.getReceiverChatIds()) {
272                 if (!sendTelegram(chatId, message, args)) {
273                     return false;
274                 }
275             }
276         }
277         return true;
278     }
279
280     @RuleAction(label = "send a photo", description = "Send a picture using the Telegram API.")
281     public boolean sendTelegramPhoto(@ActionInput(name = "chatId") @Nullable Long chatId,
282             @ActionInput(name = "photoURL") @Nullable String photoURL,
283             @ActionInput(name = "caption") @Nullable String caption) {
284         return sendTelegramPhoto(chatId, photoURL, caption, null, null);
285     }
286
287     @RuleAction(label = "send a photo", description = "Send a picture using the Telegram API.")
288     public boolean sendTelegramPhoto(@ActionInput(name = "chatId") @Nullable Long chatId,
289             @ActionInput(name = "photoURL") @Nullable String photoURL,
290             @ActionInput(name = "caption") @Nullable String caption,
291             @ActionInput(name = "username") @Nullable String username,
292             @ActionInput(name = "password") @Nullable String password) {
293         if (photoURL == null) {
294             logger.warn("Photo URL not defined; unable to retrieve photo for sending.");
295             return false;
296         }
297         if (chatId == null) {
298             logger.warn("chatId not defined; action skipped.");
299             return false;
300         }
301         String lowercasePhotoUrl = photoURL.toLowerCase();
302         TelegramHandler localHandler = handler;
303         if (localHandler != null) {
304             final SendPhoto sendPhoto;
305             if (lowercasePhotoUrl.startsWith("http")) {
306                 logger.debug("Http based URL for photo provided.");
307                 HttpClient client = localHandler.getClient();
308                 if (client == null) {
309                     return false;
310                 }
311                 Request request = client.newRequest(photoURL).method(HttpMethod.GET).timeout(30, TimeUnit.SECONDS);
312                 if (username != null && password != null) {
313                     AuthenticationStore auth = client.getAuthenticationStore();
314                     URI uri = URI.create(photoURL);
315                     auth.addAuthenticationResult(
316                             new BasicResult(HttpHeader.AUTHORIZATION, uri, "Basic " + Base64.getEncoder()
317                                     .encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8))));
318                 }
319                 try {
320                     // API has 10mb limit to jpg file size, without this it can only accept 2mb
321                     FutureResponseListener listener = new FutureResponseListener(request, 10 * 1024 * 1024);
322                     request.send(listener);
323                     ContentResponse contentResponse = listener.get();
324                     if (contentResponse.getStatus() == 200) {
325                         byte[] fileContent = contentResponse.getContent();
326                         sendPhoto = new SendPhoto(chatId, fileContent);
327                     } else {
328                         logger.warn("Download from {} failed with status: {}", photoURL, contentResponse.getStatus());
329                         sendTelegram(chatId, caption + ":Download failed with status " + contentResponse.getStatus());
330                         return false;
331                     }
332                 } catch (InterruptedException | ExecutionException e) {
333                     logger.warn("Download from {} failed with exception: {}", photoURL, e.getMessage());
334                     return false;
335                 }
336             } else if (lowercasePhotoUrl.startsWith("file:")
337                     || PHOTO_EXTENSIONS.stream().anyMatch(lowercasePhotoUrl::endsWith)) {
338                 logger.debug("Read file from local file system: {}", photoURL);
339                 String temp = photoURL;
340                 if (!lowercasePhotoUrl.startsWith("file:")) {
341                     temp = "file://" + photoURL;
342                 }
343                 try {
344                     sendPhoto = new SendPhoto(chatId, Path.of(new URL(temp).getPath()).toFile());
345                 } catch (MalformedURLException e) {
346                     logger.warn("Malformed URL: {}", photoURL);
347                     return false;
348                 }
349             } else {
350                 logger.debug("Base64 image provided; converting to binary.");
351                 final String photoB64Data;
352                 if (photoURL.startsWith("data:")) { // support data URI scheme
353                     String[] photoURLParts = photoURL.split(",");
354                     if (photoURLParts.length > 1) {
355                         photoB64Data = photoURLParts[1];
356                     } else {
357                         logger.warn("The provided base64 string is not a valid data URI scheme");
358                         return false;
359                     }
360                 } else {
361                     photoB64Data = photoURL;
362                 }
363                 InputStream is = Base64.getDecoder()
364                         .wrap(new ByteArrayInputStream(photoB64Data.getBytes(StandardCharsets.UTF_8)));
365                 try {
366                     byte[] photoBytes = is.readAllBytes();
367                     sendPhoto = new SendPhoto(chatId, photoBytes);
368                 } catch (IOException e) {
369                     logger.warn("Malformed base64 string: {}", e.getMessage());
370                     return false;
371                 }
372             }
373             if (caption != null) {
374                 sendPhoto.caption(caption);
375             }
376             if (localHandler.getParseMode() != null) {
377                 sendPhoto.parseMode(localHandler.getParseMode());
378             }
379             return evaluateResponse(localHandler.execute(sendPhoto));
380         }
381         return false;
382     }
383
384     @RuleAction(label = "send a photo", description = "Send a Picture using the Telegram API.")
385     public boolean sendTelegramPhoto(@ActionInput(name = "photoURL") @Nullable String photoURL,
386             @ActionInput(name = "caption") @Nullable String caption,
387             @ActionInput(name = "username") @Nullable String username,
388             @ActionInput(name = "password") @Nullable String password) {
389         TelegramHandler localHandler = handler;
390         if (localHandler != null) {
391             for (Long chatId : localHandler.getReceiverChatIds()) {
392                 if (!sendTelegramPhoto(chatId, photoURL, caption, username, password)) {
393                     return false;
394                 }
395             }
396         }
397         return true;
398     }
399
400     @RuleAction(label = "send a photo", description = "Send a Picture using the Telegram API.")
401     public boolean sendTelegramPhoto(@ActionInput(name = "photoURL") @Nullable String photoURL,
402             @ActionInput(name = "caption") @Nullable String caption) {
403         return sendTelegramPhoto(photoURL, caption, null, null);
404     }
405
406     @RuleAction(label = "send animation", description = "Send an Animation using the Telegram API.")
407     public boolean sendTelegramAnimation(@ActionInput(name = "animationURL") @Nullable String animationURL,
408             @ActionInput(name = "caption") @Nullable String caption) {
409         TelegramHandler localHandler = handler;
410         if (localHandler != null) {
411             for (Long chatId : localHandler.getReceiverChatIds()) {
412                 if (!sendTelegramAnimation(chatId, animationURL, caption)) {
413                     return false;
414                 }
415             }
416         }
417         return true;
418     }
419
420     @RuleAction(label = "send animation", description = "Send an Animation using the Telegram API.")
421     public boolean sendTelegramAnimation(@ActionInput(name = "chatId") @Nullable Long chatId,
422             @ActionInput(name = "animationURL") @Nullable String animationURL,
423             @ActionInput(name = "caption") @Nullable String caption) {
424         if (animationURL == null) {
425             logger.warn("Animation URL not defined; unable to retrieve video for sending.");
426             return false;
427         }
428         if (chatId == null) {
429             logger.warn("chatId not defined; action skipped.");
430             return false;
431         }
432         TelegramHandler localHandler = handler;
433         if (localHandler != null) {
434             final SendAnimation sendAnimation;
435             if (animationURL.toLowerCase().startsWith("http")) {
436                 // load image from url
437                 logger.debug("Animation URL provided.");
438                 HttpClient client = localHandler.getClient();
439                 if (client == null) {
440                     return false;
441                 }
442                 Request request = client.newRequest(animationURL).method(HttpMethod.GET).timeout(30, TimeUnit.SECONDS);
443                 try {
444                     // 50mb limit to file size
445                     FutureResponseListener listener = new FutureResponseListener(request, 50 * 1024 * 1024);
446                     request.send(listener);
447                     ContentResponse contentResponse = listener.get();
448                     if (contentResponse.getStatus() == 200) {
449                         byte[] fileContent = contentResponse.getContent();
450                         sendAnimation = new SendAnimation(chatId, fileContent);
451                     } else {
452                         logger.warn("Download from {} failed with status: {}", animationURL,
453                                 contentResponse.getStatus());
454                         sendTelegram(chatId, caption + ":Download failed with status " + contentResponse.getStatus());
455                         return false;
456                     }
457                 } catch (InterruptedException | ExecutionException e) {
458                     logger.warn("Download from {} failed with exception: {}", animationURL, e.getMessage());
459                     return false;
460                 }
461             } else {
462                 String temp = animationURL;
463                 if (!animationURL.toLowerCase().startsWith("file:")) {
464                     temp = "file://" + animationURL;
465                 }
466                 // Load video from local file system
467                 logger.debug("Read file from local file system: {}", animationURL);
468                 try {
469                     sendAnimation = new SendAnimation(chatId, Path.of(new URL(temp).getPath()).toFile());
470                 } catch (MalformedURLException e) {
471                     logger.warn("Malformed URL, should start with http or file: {}", animationURL);
472                     return false;
473                 }
474             }
475             if (caption != null) {
476                 sendAnimation.caption(caption);
477             }
478             if (localHandler.getParseMode() != null) {
479                 sendAnimation.parseMode(localHandler.getParseMode());
480             }
481             return evaluateResponse(localHandler.execute(sendAnimation));
482         }
483         return false;
484     }
485
486     @RuleAction(label = "send video", description = "Send a Video using the Telegram API.")
487     public boolean sendTelegramVideo(@ActionInput(name = "videoURL") @Nullable String videoURL,
488             @ActionInput(name = "caption") @Nullable String caption) {
489         TelegramHandler localHandler = handler;
490         if (localHandler != null) {
491             for (Long chatId : localHandler.getReceiverChatIds()) {
492                 if (!sendTelegramVideo(chatId, videoURL, caption)) {
493                     return false;
494                 }
495             }
496         }
497         return true;
498     }
499
500     @RuleAction(label = "send video", description = "Send a Video using the Telegram API.")
501     public boolean sendTelegramVideo(@ActionInput(name = "chatId") @Nullable Long chatId,
502             @ActionInput(name = "videoURL") @Nullable String videoURL,
503             @ActionInput(name = "caption") @Nullable String caption) {
504         final SendVideo sendVideo;
505         if (videoURL == null) {
506             logger.warn("Video URL not defined; unable to retrieve video for sending.");
507             return false;
508         }
509         if (chatId == null) {
510             logger.warn("chatId not defined; action skipped.");
511             return false;
512         }
513         TelegramHandler localHandler = handler;
514         if (localHandler != null) {
515             if (videoURL.toLowerCase().startsWith("http")) {
516                 logger.debug("Video http://URL provided.");
517                 HttpClient client = localHandler.getClient();
518                 if (client == null) {
519                     return false;
520                 }
521                 Request request = client.newRequest(videoURL).method(HttpMethod.GET).timeout(30, TimeUnit.SECONDS);
522                 try {
523                     // 50mb limit to file size
524                     FutureResponseListener listener = new FutureResponseListener(request, 50 * 1024 * 1024);
525                     request.send(listener);
526                     ContentResponse contentResponse = listener.get();
527                     if (contentResponse.getStatus() == 200) {
528                         byte[] fileContent = contentResponse.getContent();
529                         sendVideo = new SendVideo(chatId, fileContent);
530                     } else {
531                         logger.warn("Download from {} failed with status: {}", videoURL, contentResponse.getStatus());
532                         sendTelegram(chatId, caption + ":Download failed with status " + contentResponse.getStatus());
533                         return false;
534                     }
535                 } catch (InterruptedException | ExecutionException e) {
536                     logger.warn("Download from {} failed with exception: {}", videoURL, e.getMessage());
537                     return false;
538                 }
539             } else {
540                 String temp = videoURL;
541                 if (!videoURL.toLowerCase().startsWith("file:")) {
542                     temp = "file://" + videoURL;
543                 }
544                 // Load video from local file system with file://path
545                 logger.debug("Read file from local file: {}", videoURL);
546                 try {
547                     sendVideo = new SendVideo(chatId, Path.of(new URL(temp).getPath()).toFile());
548                 } catch (MalformedURLException e) {
549                     logger.warn("Malformed URL, should start with http or file: {}", videoURL);
550                     return false;
551                 }
552             }
553             if (caption != null) {
554                 sendVideo.caption(caption);
555             }
556             if (localHandler.getParseMode() != null) {
557                 sendVideo.parseMode(localHandler.getParseMode());
558             }
559             return evaluateResponse(localHandler.execute(sendVideo));
560         }
561         return false;
562     }
563
564     // legacy delegate methods
565     /* APIs without chatId parameter */
566     public static boolean sendTelegram(ThingActions actions, @Nullable String format, @Nullable Object... args) {
567         return ((TelegramActions) actions).sendTelegram(format, args);
568     }
569
570     public static boolean sendTelegramQuery(ThingActions actions, @Nullable String message, @Nullable String replyId,
571             @Nullable String... buttons) {
572         return ((TelegramActions) actions).sendTelegramQuery(message, replyId, buttons);
573     }
574
575     public static boolean sendTelegramPhoto(ThingActions actions, @Nullable String photoURL, @Nullable String caption) {
576         return ((TelegramActions) actions).sendTelegramPhoto(photoURL, caption, null, null);
577     }
578
579     public static boolean sendTelegramPhoto(ThingActions actions, @Nullable String photoURL, @Nullable String caption,
580             @Nullable String username, @Nullable String password) {
581         return ((TelegramActions) actions).sendTelegramPhoto(photoURL, caption, username, password);
582     }
583
584     public static boolean sendTelegramAnimation(ThingActions actions, @Nullable String animationURL,
585             @Nullable String caption) {
586         return ((TelegramActions) actions).sendTelegramVideo(animationURL, caption);
587     }
588
589     public static boolean sendTelegramVideo(ThingActions actions, @Nullable String videoURL, @Nullable String caption) {
590         return ((TelegramActions) actions).sendTelegramVideo(videoURL, caption);
591     }
592
593     public static boolean sendTelegramAnswer(ThingActions actions, @Nullable String replyId, @Nullable String message) {
594         return ((TelegramActions) actions).sendTelegramAnswer(replyId, message);
595     }
596
597     /* APIs with chatId parameter */
598
599     public static boolean sendTelegram(ThingActions actions, @Nullable Long chatId, @Nullable String format,
600             @Nullable Object... args) {
601         return ((TelegramActions) actions).sendTelegram(chatId, format, args);
602     }
603
604     public static boolean sendTelegramQuery(ThingActions actions, @Nullable Long chatId, @Nullable String message,
605             @Nullable String replyId, @Nullable String... buttons) {
606         return ((TelegramActions) actions).sendTelegramQuery(chatId, message, replyId, buttons);
607     }
608
609     public static boolean sendTelegramPhoto(ThingActions actions, @Nullable Long chatId, @Nullable String photoURL,
610             @Nullable String caption) {
611         return ((TelegramActions) actions).sendTelegramPhoto(chatId, photoURL, caption, null, null);
612     }
613
614     public static boolean sendTelegramPhoto(ThingActions actions, @Nullable Long chatId, @Nullable String photoURL,
615             @Nullable String caption, @Nullable String username, @Nullable String password) {
616         return ((TelegramActions) actions).sendTelegramPhoto(chatId, photoURL, caption, username, password);
617     }
618
619     public static boolean sendTelegramAnimation(ThingActions actions, @Nullable Long chatId,
620             @Nullable String animationURL, @Nullable String caption) {
621         return ((TelegramActions) actions).sendTelegramVideo(chatId, animationURL, caption);
622     }
623
624     public static boolean sendTelegramVideo(ThingActions actions, @Nullable Long chatId, @Nullable String videoURL,
625             @Nullable String caption) {
626         return ((TelegramActions) actions).sendTelegramVideo(chatId, videoURL, caption);
627     }
628
629     public static boolean sendTelegramAnswer(ThingActions actions, @Nullable Long chatId, @Nullable String replyId,
630             @Nullable String message) {
631         return ((TelegramActions) actions).sendTelegramAnswer(chatId, replyId, message);
632     }
633
634     public static boolean sendTelegramAnswer(ThingActions actions, @Nullable String chatId, @Nullable String replyId,
635             @Nullable String message) {
636         if (actions instanceof TelegramActions) {
637             if (chatId == null) {
638                 return false;
639             }
640             return ((TelegramActions) actions).sendTelegramAnswer(Long.valueOf(chatId), replyId, message);
641         } else {
642             throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
643         }
644     }
645
646     @Override
647     public void setThingHandler(@Nullable ThingHandler handler) {
648         this.handler = (TelegramHandler) handler;
649     }
650
651     @Override
652     public @Nullable ThingHandler getThingHandler() {
653         return handler;
654     }
655 }