/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.action.telegram.internal;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HeaderElement;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.io.IOUtils;
import org.openhab.core.scriptengine.action.ActionDoc;
import org.openhab.core.scriptengine.action.ParamDoc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class provides static methods that can be used in automation rules for
* sending Telegrams.
*
* @author Paolo Denti
* @since 1.8.0
*
*/
public class Telegram {
private static final Logger logger = LoggerFactory.getLogger(Telegram.class);
private static final String TELEGRAM_URL = "https://api.telegram.org/bot%s/sendMessage";
private static final String TELEGRAM_PHOTO_URL = "https://api.telegram.org/bot%s/sendPhoto";
private static final int HTTP_TIMEOUT = 2000;
private static final int HTTP_PHOTO_TIMEOUT = 10000;
private static Map<String, TelegramBot> groupTokens = new HashMap<String, TelegramBot>();
public static void addToken(String group, String chatId, String token) {
groupTokens.put(group, new TelegramBot(chatId, token));
}
@ActionDoc(text = "Sends a Telegram via Telegram REST API - direct message")
static public boolean sendTelegram(@ParamDoc(name = "group") String group,
@ParamDoc(name = "message") String message) {
if (groupTokens.get(group) == null) {
logger.error("Bot '{}' not defined, action skipped", group);
return false;
}
String url = String.format(TELEGRAM_URL, groupTokens.get(group).getToken());
HttpClient client = new HttpClient();
PostMethod postMethod = new PostMethod(url);
postMethod.getParams().setContentCharset("UTF-8");
postMethod.getParams().setSoTimeout(HTTP_TIMEOUT);
postMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false));
NameValuePair[] data = { new NameValuePair("chat_id", groupTokens.get(group).getChatId()),
new NameValuePair("text", message) };
postMethod.setRequestBody(data);
try {
int statusCode = client.executeMethod(postMethod);
if (statusCode == HttpStatus.SC_NO_CONTENT || statusCode == HttpStatus.SC_ACCEPTED) {
return true;
}
if (statusCode != HttpStatus.SC_OK) {
logger.warn("Method failed: {}", postMethod.getStatusLine());
return false;
}
InputStream tmpResponseStream = postMethod.getResponseBodyAsStream();
Header encodingHeader = postMethod.getResponseHeader("Content-Encoding");
if (encodingHeader != null) {
for (HeaderElement ehElem : encodingHeader.getElements()) {
if (ehElem.toString().matches(".*gzip.*")) {
tmpResponseStream = new GZIPInputStream(tmpResponseStream);
logger.debug("GZipped InputStream from {}", url);
} else if (ehElem.toString().matches(".*deflate.*")) {
tmpResponseStream = new InflaterInputStream(tmpResponseStream);
logger.debug("Deflated InputStream from {}", url);
}
}
}
String responseBody = IOUtils.toString(tmpResponseStream);
if (!responseBody.isEmpty()) {
logger.debug(responseBody);
}
} catch (HttpException e) {
logger.error("Fatal protocol violation: {}", e.toString());
return false;
} catch (IOException e) {
logger.error("Fatal transport error: {}", e.toString());
return false;
} finally {
postMethod.releaseConnection();
}
return true;
}
@ActionDoc(text = "Sends a Telegram via Telegram REST API - build message with format and args")
static public boolean sendTelegram(@ParamDoc(name = "group") String group, @ParamDoc(name = "format") String format,
@ParamDoc(name = "args") Object... args) {
return sendTelegram(group, String.format(format, args));
}
@ActionDoc(text = "Sends a Picture via Telegram REST API")
static public boolean sendTelegramPhoto(@ParamDoc(name = "group") String group,
@ParamDoc(name = "photoURL") String photoURL, @ParamDoc(name = "caption") String caption) {
return sendTelegramPhoto(group, photoURL, caption, null, null);
}
@ActionDoc(text = "Sends a Picture, protected by username/password authentication, via Telegram REST API")
static public boolean sendTelegramPhoto(@ParamDoc(name = "group") String group,
@ParamDoc(name = "photoURL") String photoURL, @ParamDoc(name = "caption") String caption,
@ParamDoc(name = "username") String username, @ParamDoc(name = "password") String password) {
if (groupTokens.get(group) == null) {
logger.error("Bot '{}' not defined, action skipped", group);
return false;
}
if (photoURL == null) {
logger.error("photoURL not defined, action skipped");
return false;
}
// load image from url
byte[] imageFromURL;
HttpClient getClient = new HttpClient();
if (username != null && password != null) {
getClient.getParams().setAuthenticationPreemptive(true);
Credentials defaultcreds = new UsernamePasswordCredentials(username, password);
getClient.getState().setCredentials(AuthScope.ANY, defaultcreds);
}
GetMethod getMethod = new GetMethod(photoURL);
getMethod.getParams().setSoTimeout(HTTP_PHOTO_TIMEOUT);
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false));
try {
int statusCode = getClient.executeMethod(getMethod);
if (statusCode != HttpStatus.SC_OK) {
logger.error("Method failed: {}", getMethod.getStatusLine());
return false;
}
imageFromURL = getMethod.getResponseBody();
} catch (HttpException e) {
logger.error("Fatal protocol violation: {}", e.toString());
return false;
} catch (IOException e) {
logger.error("Fatal transport error: {}", e.toString());
return false;
} finally {
getMethod.releaseConnection();
}
// parse image type
String imageType;
try {
ImageInputStream iis = ImageIO.createImageInputStream(new ByteArrayInputStream(imageFromURL));
Iterator<ImageReader> imageReaders = ImageIO.getImageReaders(iis);
if (!imageReaders.hasNext()) {
logger.error("photoURL does not represent a known image type");
return false;
}
ImageReader reader = imageReaders.next();
imageType = reader.getFormatName();
} catch (IOException e) {
logger.error("cannot parse photoURL as image: {}", e.getMessage());
return false;
}
// post photo to telegram
String url = String.format(TELEGRAM_PHOTO_URL, groupTokens.get(group).getToken());
PostMethod postMethod = new PostMethod(url);
try {
postMethod.getParams().setContentCharset("UTF-8");
postMethod.getParams().setSoTimeout(HTTP_PHOTO_TIMEOUT);
postMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler(3, false));
Part[] parts = new Part[caption != null ? 3 : 2];
parts[0] = new StringPart("chat_id", groupTokens.get(group).getChatId());
parts[1] = new FilePart("photo",
new ByteArrayPartSource(String.format("image.%s", imageType), imageFromURL));
if (caption != null) {
parts[2] = new StringPart("caption", caption, "UTF-8");
}
postMethod.setRequestEntity(new MultipartRequestEntity(parts, postMethod.getParams()));
HttpClient client = new HttpClient();
int statusCode = client.executeMethod(postMethod);
if (statusCode == HttpStatus.SC_NO_CONTENT || statusCode == HttpStatus.SC_ACCEPTED) {
return true;
}
if (statusCode != HttpStatus.SC_OK) {
logger.error("Method failed: {}", postMethod.getStatusLine());
return false;
}
} catch (HttpException e) {
logger.error("Fatal protocol violation: {}", e.toString());
return false;
} catch (IOException e) {
logger.error("Fatal transport error: {}", e.toString());
return false;
} finally {
postMethod.releaseConnection();
}
return true;
}
}