001/*
002 *   Copyright 2024 Vonage
003 *
004 *   Licensed under the Apache License, Version 2.0 (the "License");
005 *   you may not use this file except in compliance with the License.
006 *   You may obtain a copy of the License at
007 *
008 *        http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *   Unless required by applicable law or agreed to in writing, software
011 *   distributed under the License is distributed on an "AS IS" BASIS,
012 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *   See the License for the specific language governing permissions and
014 *   limitations under the License.
015 */
016package com.vonage.client.messages;
017
018import com.fasterxml.jackson.annotation.*;
019import com.vonage.client.Jsonable;
020import com.vonage.client.JsonableBaseObject;
021import com.vonage.client.common.UrlContainer;
022import com.vonage.client.messages.mms.Content;
023import com.vonage.client.messages.sms.SmsInboundMetadata;
024import com.vonage.client.messages.whatsapp.*;
025import java.net.URI;
026import java.time.Instant;
027import java.util.List;
028import java.util.Map;
029import java.util.UUID;
030
031/**
032 * Convenience class representing an inbound message webhook. This maps all known fields for all message types.
033 * <p>
034 * Refer to the
035 * <a href=https://developer.vonage.com/api/messages-olympus#webhooks>Messages API Webhook reference</a>
036 * for more details on the response object structure.
037 *
038 * @since 7.2.0
039 */
040public class InboundMessage extends JsonableBaseObject {
041
042        protected static class UrlWrapper extends JsonableBaseObject {
043                @JsonProperty("url") protected URI url;
044                @JsonProperty("name") protected String name;
045        }
046
047        protected static class UrlWrapperWithCaption extends UrlWrapper {
048                @JsonProperty("caption") protected String caption;
049        }
050
051        protected static class Whatsapp extends JsonableBaseObject {
052                @JsonProperty("referral") protected Referral referral;
053        }
054
055        protected static class Origin extends JsonableBaseObject {
056                @JsonProperty("network_code") protected String networkCode;
057        }
058
059        protected InboundMessage() {}
060
061        @JsonAnySetter protected Map<String, Object> unknownProperties;
062
063        @JsonProperty("timestamp") protected Instant timestamp;
064        @JsonProperty("channel") protected Channel channel;
065        @JsonProperty("message_type") protected MessageType messageType;
066        @JsonProperty("message_uuid") protected UUID messageUuid;
067        @JsonProperty("to") protected String to;
068        @JsonProperty("from") protected String from;
069        @JsonProperty("client_ref") protected String clientRef;
070        @JsonProperty("provider_message") String providerMessage;
071        @JsonProperty("_self") UrlContainer self;
072
073        @JsonProperty("text") protected String text;
074        @JsonProperty("image") protected UrlWrapperWithCaption image;
075        @JsonProperty("audio") protected UrlWrapper audio;
076        @JsonProperty("video") protected UrlWrapper video;
077        @JsonProperty("file") protected UrlWrapper file;
078        @JsonProperty("vcard") protected UrlWrapper vcard;
079        @JsonProperty("sticker") protected UrlWrapper sticker;
080        @JsonProperty("reaction") protected Reaction reaction;
081        @JsonProperty("button") protected Button button;
082        @JsonProperty("profile") protected Profile whatsappProfile;
083        @JsonProperty("context_status") protected ContextStatus whatsappContextStatus;
084        @JsonProperty("context") protected Context whatsappContext;
085        @JsonProperty("location") protected Location whatsappLocation;
086        @JsonProperty("reply") protected Reply whatsappReply;
087        @JsonProperty("order") protected Order whatsappOrder;
088        @JsonProperty("usage") protected MessageStatus.Usage usage;
089        @JsonProperty("sms") protected SmsInboundMetadata smsMetadata;
090        @JsonProperty("origin") protected Origin origin;
091        @JsonProperty("whatsapp") private Whatsapp whatsapp;
092        @JsonProperty("content") protected List<Content> content;
093
094        /**
095         * This is a catch-all method which encapsulates all fields in the response JSON
096         * that are not already a field in this class.
097         *
098         * @return The Map of unknown properties to their values.
099         */
100        @JsonAnyGetter
101        public Map<String, ?> getUnmappedProperties() {
102                return unknownProperties;
103        }
104
105        /**
106         * The media type of the message.
107         *
108         * @return The message type, as an enum.
109         */
110        public MessageType getMessageType() {
111                return messageType;
112        }
113
114        /**
115         * The service used to send the message.
116         *
117         * @return The channel, as an enum.
118         */
119        public Channel getChannel() {
120                return channel;
121        }
122
123        /**
124         * Unique identifier of the message that was sent.
125         *
126         * @return The UUID of the message.
127         */
128        public UUID getMessageUuid() {
129                return messageUuid;
130        }
131
132        /**
133         * The number this message was sent to.
134         *
135         * @return The recipient number or ID.
136         */
137        public String getTo() {
138                return to;
139        }
140
141        /**
142         * The number this message was sent from.
143         *
144         * @return The sender number or ID.
145         */
146        public String getFrom() {
147                return from;
148        }
149
150        /**
151         * The datetime of when the event occurred.
152         *
153         * @return The timestamp as an Instant.
154         */
155        public Instant getTimestamp() {
156                return timestamp;
157        }
158
159        /**
160         * The client reference will be present if set in the original outbound message.
161         *
162         * @return The client reference, or {@code null} if unset.
163         */
164        public String getClientRef() {
165                return clientRef;
166        }
167
168        /**
169         * The URL for the message resource, including the geo-specific base URI.
170         *
171         * @return The {@code _self.href} property as a URI, or {@code null} if absent.
172         * @since 8.11.0
173         */
174        @JsonIgnore
175        public URI getSelfUrl() {
176                return self != null ? self.getHref() : null;
177        }
178
179        /**
180         * If {@linkplain #getMessageType()} is {@linkplain MessageType#TEXT}, returns the message text.
181         *
182         * @return The message text, or {@code null} if not applicable.
183         */
184        public String getText() {
185                return text;
186        }
187
188        /**
189         * If {@linkplain #getMessageType()} is {@linkplain MessageType#IMAGE}, returns the URL of the image.
190         *
191         * @return The image URL, or {@code null} if not applicable.
192         */
193        @JsonIgnore
194        public URI getImageUrl() {
195                return image != null ? image.url : null;
196        }
197
198        /**
199         * Additional text accompanying the image. Applicable to MMS image messages only.
200         *
201         * @return The image caption if present, or {@code null} if not applicable.
202         *
203         * @since 8.1.0
204         */
205        @JsonIgnore
206        public String getImageCaption() {
207                return image != null ? image.caption : null;
208        }
209
210        /**
211         * If {@linkplain #getMessageType()} is {@linkplain MessageType#AUDIO}, returns the URL of the audio.
212         *
213         * @return The audio URL, or {@code null} if not applicable.
214         */
215        @JsonIgnore
216        public URI getAudioUrl() {
217                return audio != null ? audio.url : null;
218        }
219
220        /**
221         * If {@linkplain #getMessageType()} is {@linkplain MessageType#VIDEO}, returns the URL of the video.
222         *
223         * @return The video URL, or {@code null} if not applicable.
224         */
225        @JsonIgnore
226        public URI getVideoUrl() {
227                return video != null ? video.url : null;
228        }
229
230        /**
231         * If {@linkplain #getMessageType()} is {@linkplain MessageType#FILE}, returns the URL of the file.
232         *
233         * @return The file URL, or {@code null} if not applicable.
234         */
235        @JsonIgnore
236        public URI getFileUrl() {
237                return file != null ? file.url : null;
238        }
239
240        /**
241         * If {@linkplain #getMessageType()} is {@linkplain MessageType#VCARD}, returns the URL of the vCard.
242         *
243         * @return The vCard URL, or {@code null} if not applicable.
244         */
245        @JsonIgnore
246        public URI getVcardUrl() {
247                return vcard != null ? vcard.url : null;
248        }
249
250        /**
251         * If {@linkplain #getMessageType()} is {@linkplain MessageType#VCARD} and {@linkplain #getChannel()} is
252         * {@linkplain Channel#RCS}, returns the name of the vCard.
253         *
254         * @return The vCard attachment file name, or {@code null} if absent / not applicable.
255         * @since 8.11.0
256         */
257        @JsonIgnore
258        public String getVcardName() {
259                return vcard != null ? vcard.name : null;
260        }
261
262        /**
263         * If {@linkplain #getMessageType()} is {@linkplain MessageType#STICKER}, returns the URL of the sticker.
264         *
265         * @return The sticker URL, or {@code null} if not applicable.
266         */
267        @JsonIgnore
268        public URI getStickerUrl() {
269                return sticker != null ? sticker.url : null;
270        }
271
272        /**
273         * If {@linkplain #getMessageType()} is {@linkplain MessageType#REACTION}, returns the reaction.
274         *
275         * @return The reaction details, or {@code null} if not applicable.
276         * @since 8.11.0
277         */
278        public Reaction getReaction() {
279                return reaction;
280        }
281
282        /**
283         * If {@linkplain #getMessageType()} is {@linkplain MessageType#BUTTON}, returns the button.
284         *
285         * @return The button details, or {@code null} if not applicable.
286         * @since 8.11.0
287         */
288        public Button getButton() {
289                return button;
290        }
291
292        /**
293         * If {@linkplain #getChannel()} is {@linkplain Channel#WHATSAPP}, returns the {@code provider_message} field.
294         * This is a message from the channel provider, which may contain a description, error codes or other information.
295         *
296         * @return The provider message or {@code null} if not applicable.
297         */
298        public String getProviderMessage() {
299                return providerMessage;
300        }
301
302        /**
303         * If {@linkplain #getMessageType()} is {@linkplain MessageType#LOCATION} and {@linkplain #getChannel()} is
304         * {@linkplain Channel#WHATSAPP}, returns the location.
305         *
306         * @return The deserialized WhatsApp location, or {@code null} if not applicable.
307         */
308        public Location getWhatsappLocation() {
309                return whatsappLocation;
310        }
311
312        /**
313         * If {@linkplain #getMessageType()} is {@linkplain MessageType#REPLY} and {@linkplain #getChannel()} is
314         * {@linkplain Channel#WHATSAPP}, returns the reply.
315         *
316         * @return The deserialized WhatsApp reply, or {@code null} if not applicable.
317         */
318        public Reply getWhatsappReply() {
319                return whatsappReply;
320        }
321
322        /**
323         * If {@linkplain #getMessageType()} is {@linkplain MessageType#ORDER} and {@linkplain #getChannel()} is
324         * {@linkplain Channel#WHATSAPP}, returns the order.
325         *
326         * @return The deserialized WhatsApp order, or {@code null} if not applicable.
327         */
328        public Order getWhatsappOrder() {
329                return whatsappOrder;
330        }
331
332
333
334        /**
335         * If the {@linkplain #getChannel()} is {@linkplain Channel#WHATSAPP}, returns information
336         * about the sender's WhatsApp profile.
337         *
338         * @return The deserialized WhatsApp profile, or {@code null} if not applicable.
339         */
340        public Profile getWhatsappProfile() {
341                return whatsappProfile;
342        }
343
344        /**
345         * If the {@linkplain #getChannel()} is {@linkplain Channel#WHATSAPP}, returns the WhatsApp message context.
346         * This is only present where the user is quoting another message. It provides information about the quoted
347         * message and/or the product message being responded to.
348         *
349         * @return The deserialized WhatsApp context, or {@code null} if not applicable.
350         */
351        public Context getWhatsappContext() {
352                return whatsappContext;
353        }
354
355        /**
356         * If the {@linkplain #getChannel()} is {@linkplain Channel#WHATSAPP}, returns an enum indicating whether there
357         * is a context for this inbound message. If there is a context, and it is available, the context details will be
358         * contained in a context object. If there is a context, but it is unavailable,or if there is no context for
359         * message ({@linkplain ContextStatus#NONE}), then there will be no context object included in the body.
360         *
361         * @return The deserialized WhatsApp context status, or {@code null} if not applicable.
362         *
363         * @since 8.1.0
364         */
365        public ContextStatus getWhatsappContextStatus() {
366                return whatsappContextStatus;
367        }
368
369        /**
370         * If the {@linkplain #getChannel()} is {@linkplain Channel#SMS}, returns the usage
371         * information (charged incurred for the message).
372         *
373         * @return The deserialized usage object, or {@code null} if not applicable.
374         */
375        public MessageStatus.Usage getUsage() {
376                return usage;
377        }
378
379        /**
380         * If the {@linkplain #getChannel()} is {@linkplain Channel#SMS},
381         * returns additional information about the message.
382         *
383         * @return The SMS metadata, or {@code null} if not applicable.
384         */
385        public SmsInboundMetadata getSmsMetadata() {
386                return smsMetadata;
387        }
388
389        /**
390         * If the {@linkplain #getChannel()} is {@linkplain Channel#SMS} or {@linkplain Channel#MMS},
391         * return the network code from which the message originated (if available).
392         *
393         * @return The origin network code if applicable, or {@code null} if unknown.
394         *
395         * @since 8.7.0
396         */
397        @JsonIgnore
398        public String getNetworkCode() {
399                return origin != null ? origin.networkCode : null;
400        }
401
402        /**
403         * If the {@linkplain #getChannel()} is {@linkplain Channel#WHATSAPP} and a content referral is present in
404         * the message, returns the metadata related to the post or advertisement that the user clicked on.
405         *
406         * @return The Whatsapp referral object, or {@code null} if not present or applicable.
407         *
408         * @since 8.1.0
409         */
410        @JsonIgnore
411        public Referral getWhatsappReferral() {
412                return whatsapp != null ? whatsapp.referral : null;
413        }
414
415        /**
416         * If the {@linkplain #getChannel()} is {@linkplain Channel#MMS}, returns a list of one or more objects
417         * representing image, audio, video, vCard, or file attachments. Only present for messages that have
418         * more than one attachment.
419         *
420         * @return The list of MMS attachments, or {@code null} if not applicable.
421         * @since 8.11.0
422         */
423        @JsonProperty("content")
424        public List<Content> getContent() {
425                return content;
426        }
427
428        /**
429         * Constructs an instance of this class from a JSON payload. Known fields will be mapped, whilst
430         * unknown fields can be manually obtained through the {@linkplain #getUnmappedProperties()} method.
431         *
432         * @param json The webhook response JSON string.
433         *
434         * @return The deserialized webhook response object.
435         * @throws com.vonage.client.VonageResponseParseException If the response could not be deserialized.
436         */
437        @JsonCreator
438        public static InboundMessage fromJson(String json) {
439                return Jsonable.fromJson(json);
440        }
441}