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}