]> git.basschouten.com Git - openhab-addons.git/blob
e1a19e3560b7fad81e68954dc2c8eb93264e137b
[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             if (messageId == null) {
138                 logger.warn("messageId could not be found for chatId {} and replyId {}", chatId, replyId);
139                 return false;
140             }
141             logger.debug("remove messageId {} for chatId {} and replyId {}", messageId, chatId, replyId);
142
143             EditMessageReplyMarkup editReplyMarkup = new EditMessageReplyMarkup(chatId, messageId.intValue())
144                     .replyMarkup(new InlineKeyboardMarkup(new InlineKeyboardButton[0]));// remove reply markup from
145                                                                                         // old message
146             if (!evaluateResponse(localHandler.execute(editReplyMarkup))) {
147                 return false;
148             }
149             return message != null ? sendTelegram(chatId, message) : true;
150         }
151         return false;
152     }
153
154     @RuleAction(label = "send an answer", description = "Send a Telegram answer using the Telegram API.")
155     public boolean sendTelegramAnswer(@ActionInput(name = "replyId") @Nullable String replyId,
156             @ActionInput(name = "message") @Nullable String message) {
157         TelegramHandler localHandler = handler;
158         if (localHandler != null) {
159             for (Long chatId : localHandler.getReceiverChatIds()) {
160                 if (!sendTelegramAnswer(chatId, replyId, message)) {
161                     return false;
162                 }
163             }
164         }
165         return true;
166     }
167
168     @RuleAction(label = "send a message", description = "Send a Telegram message using the Telegram API.")
169     public boolean sendTelegram(@ActionInput(name = "chatId") @Nullable Long chatId,
170             @ActionInput(name = "message") @Nullable String message) {
171         return sendTelegramGeneral(chatId, message, (String) null);
172     }
173
174     @RuleAction(label = "send a message", description = "Send a Telegram message using the Telegram API.")
175     public boolean sendTelegram(@ActionInput(name = "message") @Nullable String message) {
176         TelegramHandler localHandler = handler;
177         if (localHandler != null) {
178             for (Long chatId : localHandler.getReceiverChatIds()) {
179                 if (!sendTelegram(chatId, message)) {
180                     return false;
181                 }
182             }
183         }
184         return true;
185     }
186
187     @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
188     public boolean sendTelegramQuery(@ActionInput(name = "chatId") @Nullable Long chatId,
189             @ActionInput(name = "message") @Nullable String message,
190             @ActionInput(name = "replyId") @Nullable String replyId,
191             @ActionInput(name = "buttons") @Nullable String... buttons) {
192         return sendTelegramGeneral(chatId, message, replyId, buttons);
193     }
194
195     @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
196     public boolean sendTelegramQuery(@ActionInput(name = "message") @Nullable String message,
197             @ActionInput(name = "replyId") @Nullable String replyId,
198             @ActionInput(name = "buttons") @Nullable String... buttons) {
199         TelegramHandler localHandler = handler;
200         if (localHandler != null) {
201             for (Long chatId : localHandler.getReceiverChatIds()) {
202                 if (!sendTelegramQuery(chatId, message, replyId, buttons)) {
203                     return false;
204                 }
205             }
206         }
207         return true;
208     }
209
210     private boolean sendTelegramGeneral(@ActionInput(name = "chatId") @Nullable Long chatId, @Nullable String message,
211             @Nullable String replyId, @Nullable String... buttons) {
212         if (message == null) {
213             logger.warn("Message not defined; action skipped.");
214             return false;
215         }
216         if (chatId == null) {
217             logger.warn("chatId not defined; action skipped.");
218             return false;
219         }
220         TelegramHandler localHandler = handler;
221         if (localHandler != null) {
222             SendMessage sendMessage = new SendMessage(chatId, message);
223             if (localHandler.getParseMode() != null) {
224                 sendMessage.parseMode(localHandler.getParseMode());
225             }
226             if (replyId != null) {
227                 if (!replyId.contains(" ")) {
228                     if (buttons.length > 0) {
229                         InlineKeyboardButton[][] keyboard2D = new InlineKeyboardButton[1][];
230                         InlineKeyboardButton[] keyboard = new InlineKeyboardButton[buttons.length];
231                         keyboard2D[0] = keyboard;
232                         for (int i = 0; i < buttons.length; i++) {
233                             keyboard[i] = new InlineKeyboardButton(buttons[i]).callbackData(replyId + " " + buttons[i]);
234                         }
235                         InlineKeyboardMarkup keyBoardMarkup = new InlineKeyboardMarkup(keyboard2D);
236                         sendMessage.replyMarkup(keyBoardMarkup);
237                     } else {
238                         logger.warn(
239                                 "The replyId {} for message {} is given, but no buttons are defined. ReplyMarkup will be ignored.",
240                                 replyId, message);
241                     }
242                 } else {
243                     logger.warn("replyId {} must not contain spaces. ReplyMarkup will be ignored.", replyId);
244                 }
245             }
246             SendResponse retMessage = null;
247             try {
248                 retMessage = localHandler.execute(sendMessage);
249             } catch (Exception e) {
250                 logger.warn("Exception occured whilst sending message:{}", e.getMessage());
251             }
252             if (!evaluateResponse(retMessage)) {
253                 return false;
254             }
255             if (replyId != null && retMessage != null) {
256                 logger.debug("Adding chatId {}, replyId {} and messageId {}", chatId, replyId,
257                         retMessage.message().messageId());
258                 localHandler.addMessageId(chatId, replyId, retMessage.message().messageId());
259             }
260             return true;
261         }
262         return false;
263     }
264
265     @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
266     public boolean sendTelegram(@ActionInput(name = "chatId") @Nullable Long chatId,
267             @ActionInput(name = "message") @Nullable String message,
268             @ActionInput(name = "args") @Nullable Object... args) {
269         if (message == null) {
270             return false;
271         }
272         return sendTelegram(chatId, String.format(message, args));
273     }
274
275     @RuleAction(label = "send a message", description = "Send a Telegram using the Telegram API.")
276     public boolean sendTelegram(@ActionInput(name = "message") @Nullable String message,
277             @ActionInput(name = "args") @Nullable Object... args) {
278         TelegramHandler localHandler = handler;
279         if (localHandler != null) {
280             for (Long chatId : localHandler.getReceiverChatIds()) {
281                 if (!sendTelegram(chatId, message, args)) {
282                     return false;
283                 }
284             }
285         }
286         return true;
287     }
288
289     @RuleAction(label = "send a photo", description = "Send a picture using the Telegram API.")
290     public boolean sendTelegramPhoto(@ActionInput(name = "chatId") @Nullable Long chatId,
291             @ActionInput(name = "photoURL") @Nullable String photoURL,
292             @ActionInput(name = "caption") @Nullable String caption) {
293         return sendTelegramPhoto(chatId, photoURL, caption, null, null);
294     }
295
296     @RuleAction(label = "send a photo", description = "Send a picture using the Telegram API.")
297     public boolean sendTelegramPhoto(@ActionInput(name = "chatId") @Nullable Long chatId,
298             @ActionInput(name = "photoURL") @Nullable String photoURL,
299             @ActionInput(name = "caption") @Nullable String caption,
300             @ActionInput(name = "username") @Nullable String username,
301             @ActionInput(name = "password") @Nullable String password) {
302         if (photoURL == null) {
303             logger.warn("Photo URL not defined; unable to retrieve photo for sending.");
304             return false;
305         }
306         if (chatId == null) {
307             logger.warn("chatId not defined; action skipped.");
308             return false;
309         }
310         String lowercasePhotoUrl = photoURL.toLowerCase();
311         TelegramHandler localHandler = handler;
312         if (localHandler != null) {
313             final SendPhoto sendPhoto;
314             if (lowercasePhotoUrl.startsWith("http")) {
315                 logger.debug("Http based URL for photo provided.");
316                 HttpClient client = localHandler.getClient();
317                 if (client == null) {
318                     return false;
319                 }
320                 Request request = client.newRequest(photoURL).method(HttpMethod.GET).timeout(30, TimeUnit.SECONDS);
321                 if (username != null && password != null) {
322                     AuthenticationStore auth = client.getAuthenticationStore();
323                     URI uri = URI.create(photoURL);
324                     auth.addAuthenticationResult(
325                             new BasicResult(HttpHeader.AUTHORIZATION, uri, "Basic " + Base64.getEncoder()
326                                     .encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8))));
327                 }
328                 try {
329                     // API has 10mb limit to jpg file size, without this it can only accept 2mb
330                     FutureResponseListener listener = new FutureResponseListener(request, 10 * 1024 * 1024);
331                     request.send(listener);
332                     ContentResponse contentResponse = listener.get();
333                     if (contentResponse.getStatus() == 200) {
334                         byte[] fileContent = contentResponse.getContent();
335                         sendPhoto = new SendPhoto(chatId, fileContent);
336                     } else {
337                         logger.warn("Download from {} failed with status: {}", photoURL, contentResponse.getStatus());
338                         sendTelegram(chatId, caption + ":Download failed with status " + contentResponse.getStatus());
339                         return false;
340                     }
341                 } catch (InterruptedException | ExecutionException e) {
342                     logger.warn("Download from {} failed with exception: {}", photoURL, e.getMessage());
343                     return false;
344                 }
345             } else if (lowercasePhotoUrl.startsWith("file:")
346                     || PHOTO_EXTENSIONS.stream().anyMatch(lowercasePhotoUrl::endsWith)) {
347                 logger.debug("Read file from local file system: {}", photoURL);
348                 String temp = photoURL;
349                 if (!lowercasePhotoUrl.startsWith("file:")) {
350                     temp = "file://" + photoURL;
351                 }
352                 try {
353                     sendPhoto = new SendPhoto(chatId, Path.of(new URL(temp).getPath()).toFile());
354                 } catch (MalformedURLException e) {
355                     logger.warn("Malformed URL: {}", photoURL);
356                     return false;
357                 }
358             } else {
359                 logger.debug("Base64 image provided; converting to binary.");
360                 final String photoB64Data;
361                 if (photoURL.startsWith("data:")) { // support data URI scheme
362                     String[] photoURLParts = photoURL.split(",");
363                     if (photoURLParts.length > 1) {
364                         photoB64Data = photoURLParts[1];
365                     } else {
366                         logger.warn("The provided base64 string is not a valid data URI scheme");
367                         return false;
368                     }
369                 } else {
370                     photoB64Data = photoURL;
371                 }
372                 InputStream is = Base64.getDecoder()
373                         .wrap(new ByteArrayInputStream(photoB64Data.getBytes(StandardCharsets.UTF_8)));
374                 try {
375                     byte[] photoBytes = is.readAllBytes();
376                     sendPhoto = new SendPhoto(chatId, photoBytes);
377                 } catch (IOException e) {
378                     logger.warn("Malformed base64 string: {}", e.getMessage());
379                     return false;
380                 }
381             }
382             if (caption != null) {
383                 sendPhoto.caption(caption);
384             }
385             if (localHandler.getParseMode() != null) {
386                 sendPhoto.parseMode(localHandler.getParseMode());
387             }
388             return evaluateResponse(localHandler.execute(sendPhoto));
389         }
390         return false;
391     }
392
393     @RuleAction(label = "send a photo", description = "Send a Picture using the Telegram API.")
394     public boolean sendTelegramPhoto(@ActionInput(name = "photoURL") @Nullable String photoURL,
395             @ActionInput(name = "caption") @Nullable String caption,
396             @ActionInput(name = "username") @Nullable String username,
397             @ActionInput(name = "password") @Nullable String password) {
398         TelegramHandler localHandler = handler;
399         if (localHandler != null) {
400             for (Long chatId : localHandler.getReceiverChatIds()) {
401                 if (!sendTelegramPhoto(chatId, photoURL, caption, username, password)) {
402                     return false;
403                 }
404             }
405         }
406         return true;
407     }
408
409     @RuleAction(label = "send a photo", description = "Send a Picture using the Telegram API.")
410     public boolean sendTelegramPhoto(@ActionInput(name = "photoURL") @Nullable String photoURL,
411             @ActionInput(name = "caption") @Nullable String caption) {
412         return sendTelegramPhoto(photoURL, caption, null, null);
413     }
414
415     @RuleAction(label = "send animation", description = "Send an Animation using the Telegram API.")
416     public boolean sendTelegramAnimation(@ActionInput(name = "animationURL") @Nullable String animationURL,
417             @ActionInput(name = "caption") @Nullable String caption) {
418         TelegramHandler localHandler = handler;
419         if (localHandler != null) {
420             for (Long chatId : localHandler.getReceiverChatIds()) {
421                 if (!sendTelegramAnimation(chatId, animationURL, caption)) {
422                     return false;
423                 }
424             }
425         }
426         return true;
427     }
428
429     @RuleAction(label = "send animation", description = "Send an Animation using the Telegram API.")
430     public boolean sendTelegramAnimation(@ActionInput(name = "chatId") @Nullable Long chatId,
431             @ActionInput(name = "animationURL") @Nullable String animationURL,
432             @ActionInput(name = "caption") @Nullable String caption) {
433         if (animationURL == null) {
434             logger.warn("Animation URL not defined; unable to retrieve video for sending.");
435             return false;
436         }
437         if (chatId == null) {
438             logger.warn("chatId not defined; action skipped.");
439             return false;
440         }
441         TelegramHandler localHandler = handler;
442         if (localHandler != null) {
443             final SendAnimation sendAnimation;
444             if (animationURL.toLowerCase().startsWith("http")) {
445                 // load image from url
446                 logger.debug("Animation URL provided.");
447                 HttpClient client = localHandler.getClient();
448                 if (client == null) {
449                     return false;
450                 }
451                 Request request = client.newRequest(animationURL).method(HttpMethod.GET).timeout(30, TimeUnit.SECONDS);
452                 try {
453                     // 50mb limit to file size
454                     FutureResponseListener listener = new FutureResponseListener(request, 50 * 1024 * 1024);
455                     request.send(listener);
456                     ContentResponse contentResponse = listener.get();
457                     if (contentResponse.getStatus() == 200) {
458                         byte[] fileContent = contentResponse.getContent();
459                         sendAnimation = new SendAnimation(chatId, fileContent);
460                     } else {
461                         logger.warn("Download from {} failed with status: {}", animationURL,
462                                 contentResponse.getStatus());
463                         sendTelegram(chatId, caption + ":Download failed with status " + contentResponse.getStatus());
464                         return false;
465                     }
466                 } catch (InterruptedException | ExecutionException e) {
467                     logger.warn("Download from {} failed with exception: {}", animationURL, e.getMessage());
468                     return false;
469                 }
470             } else {
471                 String temp = animationURL;
472                 if (!animationURL.toLowerCase().startsWith("file:")) {
473                     temp = "file://" + animationURL;
474                 }
475                 // Load video from local file system
476                 logger.debug("Read file from local file system: {}", animationURL);
477                 try {
478                     sendAnimation = new SendAnimation(chatId, Path.of(new URL(temp).getPath()).toFile());
479                 } catch (MalformedURLException e) {
480                     logger.warn("Malformed URL, should start with http or file: {}", animationURL);
481                     return false;
482                 }
483             }
484             if (caption != null) {
485                 sendAnimation.caption(caption);
486             }
487             if (localHandler.getParseMode() != null) {
488                 sendAnimation.parseMode(localHandler.getParseMode());
489             }
490             return evaluateResponse(localHandler.execute(sendAnimation));
491         }
492         return false;
493     }
494
495     @RuleAction(label = "send video", description = "Send a Video using the Telegram API.")
496     public boolean sendTelegramVideo(@ActionInput(name = "videoURL") @Nullable String videoURL,
497             @ActionInput(name = "caption") @Nullable String caption) {
498         TelegramHandler localHandler = handler;
499         if (localHandler != null) {
500             for (Long chatId : localHandler.getReceiverChatIds()) {
501                 if (!sendTelegramVideo(chatId, videoURL, caption)) {
502                     return false;
503                 }
504             }
505         }
506         return true;
507     }
508
509     @RuleAction(label = "send video", description = "Send a Video using the Telegram API.")
510     public boolean sendTelegramVideo(@ActionInput(name = "chatId") @Nullable Long chatId,
511             @ActionInput(name = "videoURL") @Nullable String videoURL,
512             @ActionInput(name = "caption") @Nullable String caption) {
513         final SendVideo sendVideo;
514         if (videoURL == null) {
515             logger.warn("Video URL not defined; unable to retrieve video for sending.");
516             return false;
517         }
518         if (chatId == null) {
519             logger.warn("chatId not defined; action skipped.");
520             return false;
521         }
522         TelegramHandler localHandler = handler;
523         if (localHandler != null) {
524             if (videoURL.toLowerCase().startsWith("http")) {
525                 logger.debug("Video http://URL provided.");
526                 HttpClient client = localHandler.getClient();
527                 if (client == null) {
528                     return false;
529                 }
530                 Request request = client.newRequest(videoURL).method(HttpMethod.GET).timeout(30, TimeUnit.SECONDS);
531                 try {
532                     // 50mb limit to file size
533                     FutureResponseListener listener = new FutureResponseListener(request, 50 * 1024 * 1024);
534                     request.send(listener);
535                     ContentResponse contentResponse = listener.get();
536                     if (contentResponse.getStatus() == 200) {
537                         byte[] fileContent = contentResponse.getContent();
538                         sendVideo = new SendVideo(chatId, fileContent);
539                     } else {
540                         logger.warn("Download from {} failed with status: {}", videoURL, contentResponse.getStatus());
541                         sendTelegram(chatId, caption + ":Download failed with status " + contentResponse.getStatus());
542                         return false;
543                     }
544                 } catch (InterruptedException | ExecutionException e) {
545                     logger.warn("Download from {} failed with exception: {}", videoURL, e.getMessage());
546                     return false;
547                 }
548             } else {
549                 String temp = videoURL;
550                 if (!videoURL.toLowerCase().startsWith("file:")) {
551                     temp = "file://" + videoURL;
552                 }
553                 // Load video from local file system with file://path
554                 logger.debug("Read file from local file: {}", videoURL);
555                 try {
556                     sendVideo = new SendVideo(chatId, Path.of(new URL(temp).getPath()).toFile());
557                 } catch (MalformedURLException e) {
558                     logger.warn("Malformed URL, should start with http or file: {}", videoURL);
559                     return false;
560                 }
561             }
562             if (caption != null) {
563                 sendVideo.caption(caption);
564             }
565             if (localHandler.getParseMode() != null) {
566                 sendVideo.parseMode(localHandler.getParseMode());
567             }
568             return evaluateResponse(localHandler.execute(sendVideo));
569         }
570         return false;
571     }
572
573     // legacy delegate methods
574     /* APIs without chatId parameter */
575     public static boolean sendTelegram(ThingActions actions, @Nullable String format, @Nullable Object... args) {
576         return ((TelegramActions) actions).sendTelegram(format, args);
577     }
578
579     public static boolean sendTelegramQuery(ThingActions actions, @Nullable String message, @Nullable String replyId,
580             @Nullable String... buttons) {
581         return ((TelegramActions) actions).sendTelegramQuery(message, replyId, buttons);
582     }
583
584     public static boolean sendTelegramPhoto(ThingActions actions, @Nullable String photoURL, @Nullable String caption) {
585         return ((TelegramActions) actions).sendTelegramPhoto(photoURL, caption, null, null);
586     }
587
588     public static boolean sendTelegramPhoto(ThingActions actions, @Nullable String photoURL, @Nullable String caption,
589             @Nullable String username, @Nullable String password) {
590         return ((TelegramActions) actions).sendTelegramPhoto(photoURL, caption, username, password);
591     }
592
593     public static boolean sendTelegramAnimation(ThingActions actions, @Nullable String animationURL,
594             @Nullable String caption) {
595         return ((TelegramActions) actions).sendTelegramVideo(animationURL, caption);
596     }
597
598     public static boolean sendTelegramVideo(ThingActions actions, @Nullable String videoURL, @Nullable String caption) {
599         return ((TelegramActions) actions).sendTelegramVideo(videoURL, caption);
600     }
601
602     public static boolean sendTelegramAnswer(ThingActions actions, @Nullable String replyId, @Nullable String message) {
603         return ((TelegramActions) actions).sendTelegramAnswer(replyId, message);
604     }
605
606     /* APIs with chatId parameter */
607
608     public static boolean sendTelegram(ThingActions actions, @Nullable Long chatId, @Nullable String format,
609             @Nullable Object... args) {
610         return ((TelegramActions) actions).sendTelegram(chatId, format, args);
611     }
612
613     public static boolean sendTelegramQuery(ThingActions actions, @Nullable Long chatId, @Nullable String message,
614             @Nullable String replyId, @Nullable String... buttons) {
615         return ((TelegramActions) actions).sendTelegramQuery(chatId, message, replyId, buttons);
616     }
617
618     public static boolean sendTelegramPhoto(ThingActions actions, @Nullable Long chatId, @Nullable String photoURL,
619             @Nullable String caption) {
620         return ((TelegramActions) actions).sendTelegramPhoto(chatId, photoURL, caption, null, null);
621     }
622
623     public static boolean sendTelegramPhoto(ThingActions actions, @Nullable Long chatId, @Nullable String photoURL,
624             @Nullable String caption, @Nullable String username, @Nullable String password) {
625         return ((TelegramActions) actions).sendTelegramPhoto(chatId, photoURL, caption, username, password);
626     }
627
628     public static boolean sendTelegramAnimation(ThingActions actions, @Nullable Long chatId,
629             @Nullable String animationURL, @Nullable String caption) {
630         return ((TelegramActions) actions).sendTelegramVideo(chatId, animationURL, caption);
631     }
632
633     public static boolean sendTelegramVideo(ThingActions actions, @Nullable Long chatId, @Nullable String videoURL,
634             @Nullable String caption) {
635         return ((TelegramActions) actions).sendTelegramVideo(chatId, videoURL, caption);
636     }
637
638     public static boolean sendTelegramAnswer(ThingActions actions, @Nullable Long chatId, @Nullable String replyId,
639             @Nullable String message) {
640         return ((TelegramActions) actions).sendTelegramAnswer(chatId, replyId, message);
641     }
642
643     public static boolean sendTelegramAnswer(ThingActions actions, @Nullable String chatId, @Nullable String replyId,
644             @Nullable String message) {
645         if (actions instanceof TelegramActions) {
646             if (chatId == null) {
647                 return false;
648             }
649             return ((TelegramActions) actions).sendTelegramAnswer(Long.valueOf(chatId), replyId, message);
650         } else {
651             throw new IllegalArgumentException("Actions is not an instance of TelegramActions");
652         }
653     }
654
655     @Override
656     public void setThingHandler(@Nullable ThingHandler handler) {
657         this.handler = (TelegramHandler) handler;
658     }
659
660     @Override
661     public @Nullable ThingHandler getThingHandler() {
662         return handler;
663     }
664 }