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