2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.pushover.internal.connection;
16 import java.io.IOException;
17 import java.util.Arrays;
18 import java.util.List;
19 import java.util.stream.Collectors;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.eclipse.jetty.client.api.ContentProvider;
24 import org.eclipse.jetty.client.util.MultiPartContentProvider;
25 import org.eclipse.jetty.client.util.PathContentProvider;
26 import org.eclipse.jetty.client.util.StringContentProvider;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
31 * The {@link PushoverMessageBuilder} builds the body for Pushover Messages API requests.
33 * @author Christoph Weitkamp - Initial contribution
36 public class PushoverMessageBuilder {
38 private final Logger logger = LoggerFactory.getLogger(PushoverMessageBuilder.class);
40 public static final String MESSAGE_KEY_TOKEN = "token";
41 private static final String MESSAGE_KEY_USER = "user";
42 private static final String MESSAGE_KEY_MESSAGE = "message";
43 private static final String MESSAGE_KEY_TITLE = "title";
44 private static final String MESSAGE_KEY_DEVICE = "device";
45 private static final String MESSAGE_KEY_PRIORITY = "priority";
46 private static final String MESSAGE_KEY_RETRY = "retry";
47 private static final String MESSAGE_KEY_EXPIRE = "expire";
48 private static final String MESSAGE_KEY_URL = "url";
49 private static final String MESSAGE_KEY_URL_TITLE = "url_title";
50 private static final String MESSAGE_KEY_SOUND = "sound";
51 private static final String MESSAGE_KEY_ATTACHMENT = "attachment";
52 public static final String MESSAGE_KEY_HTML = "html";
53 public static final String MESSAGE_KEY_MONOSPACE = "monospace";
55 private static final int MAX_MESSAGE_LENGTH = 1024;
56 private static final int MAX_TITLE_LENGTH = 250;
57 private static final int MAX_DEVICE_LENGTH = 25;
58 private static final List<Integer> VALID_PRIORITY_LIST = Arrays.asList(-2, -1, 0, 1, 2);
59 private static final int DEFAULT_PRIORITY = 0;
60 public static final int EMERGENCY_PRIORITY = 2;
61 private static final int MIN_RETRY_SECONDS = 30;
62 private static final int MAX_EXPIRE_SECONDS = 10800;
63 private static final int MAX_URL_LENGTH = 512;
64 private static final int MAX_URL_TITLE_LENGTH = 100;
65 public static final String DEFAULT_CONTENT_TYPE = "image/jpeg";
67 private final MultiPartContentProvider body = new MultiPartContentProvider();
69 private @Nullable String message;
70 private @Nullable String title;
71 private @Nullable String device;
72 private int priority = DEFAULT_PRIORITY;
73 private int retry = 300;
74 private int expire = 3600;
75 private @Nullable String url;
76 private @Nullable String urlTitle;
77 private @Nullable String sound;
78 private @Nullable String attachment;
79 private String contentType = DEFAULT_CONTENT_TYPE;
80 private boolean html = false;
81 private boolean monospace = false;
83 private PushoverMessageBuilder(String apikey, String user) throws PushoverConfigurationException {
84 body.addFieldPart(MESSAGE_KEY_TOKEN, new StringContentProvider(apikey), null);
85 body.addFieldPart(MESSAGE_KEY_USER, new StringContentProvider(user), null);
88 public static PushoverMessageBuilder getInstance(@Nullable String apikey, @Nullable String user)
89 throws PushoverConfigurationException {
90 if (apikey == null || apikey.isEmpty()) {
91 throw new PushoverConfigurationException("@text/offline.conf-error-missing-apikey");
94 if (user == null || user.isEmpty()) {
95 throw new PushoverConfigurationException("@text/offline.conf-error-missing-user");
98 return new PushoverMessageBuilder(apikey, user);
101 public PushoverMessageBuilder withMessage(String message) {
102 this.message = message;
106 public PushoverMessageBuilder withTitle(String title) {
111 public PushoverMessageBuilder withDevice(String device) {
112 this.device = device;
116 public PushoverMessageBuilder withPriority(int priority) {
117 this.priority = priority;
121 public PushoverMessageBuilder withRetry(int retry) {
126 public PushoverMessageBuilder withExpire(int expire) {
127 this.expire = expire;
131 public PushoverMessageBuilder withUrl(String url) {
136 public PushoverMessageBuilder withUrlTitle(String urlTitle) {
137 this.urlTitle = urlTitle;
141 public PushoverMessageBuilder withSound(String sound) {
146 public PushoverMessageBuilder withAttachment(String attachment) {
147 this.attachment = attachment;
151 public PushoverMessageBuilder withContentType(String contentType) {
152 this.contentType = contentType;
156 public PushoverMessageBuilder withHtmlFormatting() {
161 public PushoverMessageBuilder withMonospaceFormatting() {
162 this.monospace = true;
166 public ContentProvider build() throws PushoverCommunicationException {
167 if (message != null) {
168 if (message.length() > MAX_MESSAGE_LENGTH) {
169 throw new IllegalArgumentException(String.format(
170 "Skip sending the message as 'message' is longer than %d characters.", MAX_MESSAGE_LENGTH));
172 body.addFieldPart(MESSAGE_KEY_MESSAGE, new StringContentProvider(message), null);
176 if (title.length() > MAX_TITLE_LENGTH) {
177 throw new IllegalArgumentException(String
178 .format("Skip sending the message as 'title' is longer than %d characters.", MAX_TITLE_LENGTH));
180 body.addFieldPart(MESSAGE_KEY_TITLE, new StringContentProvider(title), null);
183 if (device != null) {
184 if (device.length() > MAX_DEVICE_LENGTH) {
185 logger.warn("Skip 'device' as it is longer than {} characters. Got: {}.", MAX_DEVICE_LENGTH, device);
187 body.addFieldPart(MESSAGE_KEY_DEVICE, new StringContentProvider(device), null);
191 if (priority != DEFAULT_PRIORITY) {
192 if (VALID_PRIORITY_LIST.contains(priority)) {
193 body.addFieldPart(MESSAGE_KEY_PRIORITY, new StringContentProvider(String.valueOf(priority)), null);
195 if (priority == EMERGENCY_PRIORITY) {
196 if (retry < MIN_RETRY_SECONDS) {
197 logger.warn("Retry value of {} is too small. Using default value of {}.", retry,
199 body.addFieldPart(MESSAGE_KEY_RETRY,
200 new StringContentProvider(String.valueOf(MIN_RETRY_SECONDS)), null);
202 body.addFieldPart(MESSAGE_KEY_RETRY, new StringContentProvider(String.valueOf(retry)), null);
205 if (0 < expire && expire <= MAX_EXPIRE_SECONDS) {
206 body.addFieldPart(MESSAGE_KEY_EXPIRE, new StringContentProvider(String.valueOf(expire)), null);
208 logger.warn("Expire value of {} is invalid. Using default value of {}.", expire,
210 body.addFieldPart(MESSAGE_KEY_EXPIRE,
211 new StringContentProvider(String.valueOf(MAX_EXPIRE_SECONDS)), null);
215 logger.warn("Invalid 'priority', skipping. Expected: {}. Got: {}.",
216 VALID_PRIORITY_LIST.stream().map(i -> i.toString()).collect(Collectors.joining(",")), priority);
221 if (url.length() > MAX_URL_LENGTH) {
222 throw new IllegalArgumentException(String
223 .format("Skip sending the message as 'url' is longer than %d characters.", MAX_URL_LENGTH));
225 body.addFieldPart(MESSAGE_KEY_URL, new StringContentProvider(url), null);
227 if (urlTitle != null) {
228 if (urlTitle.length() > MAX_URL_TITLE_LENGTH) {
229 throw new IllegalArgumentException(
230 String.format("Skip sending the message as 'urlTitle' is longer than %d characters.",
231 MAX_URL_TITLE_LENGTH));
233 body.addFieldPart(MESSAGE_KEY_URL_TITLE, new StringContentProvider(urlTitle), null);
238 body.addFieldPart(MESSAGE_KEY_SOUND, new StringContentProvider(sound), null);
241 if (attachment != null) {
242 File file = new File(attachment);
243 if (!file.exists()) {
244 throw new IllegalArgumentException(
245 String.format("Skip sending the message as file '%s' does not exist.", attachment));
248 body.addFilePart(MESSAGE_KEY_ATTACHMENT, file.getName(),
249 new PathContentProvider(contentType, file.toPath()), null);
250 } catch (IOException e) {
251 logger.debug("IOException occurred - skip sending message: {}", e.getLocalizedMessage(), e);
252 throw new PushoverCommunicationException(
253 String.format("Skip sending the message: %s", e.getLocalizedMessage()), e);
258 body.addFieldPart(MESSAGE_KEY_HTML, new StringContentProvider("1"), null);
259 } else if (monospace) {
260 body.addFieldPart(MESSAGE_KEY_MONOSPACE, new StringContentProvider("1"), null);