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.voice; 017 018import com.fasterxml.jackson.annotation.JsonProperty; 019import com.vonage.client.Jsonable; 020import com.vonage.client.JsonableBaseObject; 021import com.vonage.client.common.HttpMethod; 022import com.vonage.client.voice.ncco.Action; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.Collection; 026 027/** 028 * Call encapsulates the information required to create a call using {@link VoiceClient#createCall(Call)} 029 */ 030public class Call extends JsonableBaseObject { 031 private Endpoint[] to; 032 private Endpoint from; 033 private HttpMethod answerMethod = HttpMethod.GET, eventMethod; 034 private String answerUrl, eventUrl; 035 private MachineDetection machineDetection; 036 private AdvancedMachineDetection advancedMachineDetection; 037 private Integer lengthTimer, ringingTimer; 038 private Boolean fromRandomNumber; 039 private Collection<? extends Action> ncco; 040 041 /** 042 * Builder pattern constructor. 043 * 044 * @param builder The builder to instantiate from. 045 * @since 7.3.0 046 */ 047 Call(Builder builder) { 048 if ((to = builder.to) == null || to.length == 0 || to[0] == null) { 049 throw new IllegalStateException("At least one recipient should be specified."); 050 } 051 if ((lengthTimer = builder.lengthTimer) != null && (lengthTimer > 7200 || lengthTimer < 1)) { 052 throw new IllegalArgumentException("Length timer must be between 1 and 7200."); 053 } 054 if ((ringingTimer = builder.ringingTimer) != null && (ringingTimer > 120 || ringingTimer < 1)) { 055 throw new IllegalArgumentException("Ringing timer must be between 1 and 120."); 056 } 057 if (builder.answerMethod != null) switch (answerMethod = builder.answerMethod) { 058 case GET: case POST: break; 059 default: throw new IllegalArgumentException("Answer method must be GET or POST"); 060 } 061 if ((eventMethod = builder.eventMethod) != null) switch (eventMethod) { 062 case GET: case POST: break; 063 default: throw new IllegalArgumentException("Event method must be GET or POST"); 064 } 065 if ((from = builder.from) == null) { 066 fromRandomNumber = true; 067 } 068 else if ((fromRandomNumber = builder.fromRandomNumber) != null && fromRandomNumber) { 069 throw new IllegalStateException("From number shouldn't be set if using random."); 070 } 071 answerUrl = builder.answerUrl; 072 eventUrl = builder.eventUrl; 073 ncco = builder.ncco; 074 advancedMachineDetection = builder.advancedMachineDetection; 075 if ((machineDetection = builder.machineDetection) != null && advancedMachineDetection != null) { 076 throw new IllegalStateException("Cannot set both machineDetection and advancedMachineDetection."); 077 } 078 } 079 080 Call() {} 081 082 public Call(String to, String from, String answerUrl) { 083 this(new PhoneEndpoint(to), new PhoneEndpoint(from), answerUrl); 084 } 085 086 public Call(Endpoint to, Endpoint from, String answerUrl) { 087 this(new Endpoint[]{to}, from, answerUrl); 088 } 089 090 public Call(String to, String from, Collection<? extends Action> ncco) { 091 this(new PhoneEndpoint(to), new PhoneEndpoint(from), ncco); 092 } 093 094 public Call(Endpoint to, Endpoint from, Collection<? extends Action> ncco) { 095 this(new Endpoint[]{to}, from, ncco); 096 } 097 098 public Call(Endpoint[] to, Endpoint from, String answerUrl) { 099 this(builder().to(to).from(from).answerUrl(answerUrl)); 100 } 101 102 public Call(Endpoint[] to, Endpoint from, Collection<? extends Action> ncco) { 103 this(builder().to(to).from(from).ncco(ncco)); 104 } 105 106 @JsonProperty("to") 107 public Endpoint[] getTo() { 108 return to; 109 } 110 111 @JsonProperty("from") 112 public Endpoint getFrom() { 113 return from; 114 } 115 116 @JsonProperty("answer_url") 117 public String[] getAnswerUrl() { 118 return (answerUrl != null) ? new String[]{answerUrl} : null; 119 } 120 121 @JsonProperty("answer_method") 122 public String getAnswerMethod() { 123 // Hide the answer method if the answer url isn't defined 124 if (answerUrl == null || answerMethod == null) return null; 125 return answerMethod.toString(); 126 } 127 128 @JsonProperty("event_url") 129 public String[] getEventUrl() { 130 if (eventUrl == null) { 131 return null; 132 } 133 return new String[]{eventUrl}; 134 } 135 136 @JsonProperty("event_method") 137 public String getEventMethod() { 138 return eventMethod != null ? eventMethod.toString() : null; 139 } 140 141 @JsonProperty("machine_detection") 142 public MachineDetection getMachineDetection() { 143 return machineDetection; 144 } 145 146 /** 147 * Premium machine detection, overrides {@link #getMachineDetection()} if both are set. 148 * 149 * @return The advanced machine detection settings, or {@code null} if not set. 150 * 151 * @since 7.4.0 152 */ 153 @JsonProperty("advanced_machine_detection") 154 public AdvancedMachineDetection getAdvancedMachineDetection() { 155 return advancedMachineDetection; 156 } 157 158 @JsonProperty("length_timer") 159 public Integer getLengthTimer() { 160 return lengthTimer; 161 } 162 163 @JsonProperty("ringing_timer") 164 public Integer getRingingTimer() { 165 return ringingTimer; 166 } 167 168 @JsonProperty("random_from_number") 169 public Boolean getFromRandomNumber() { 170 return fromRandomNumber; 171 } 172 173 /** 174 * 175 * @return The NCCO actions. 176 */ 177 @JsonProperty("ncco") 178 public Collection<? extends Action> getNcco() { 179 return ncco; 180 } 181 182 /** 183 * Creates an instance of this class from a JSON payload. 184 * 185 * @param json The JSON string to parse. 186 * 187 * @return An instance of this class with the fields populated, if present. 188 */ 189 public static Call fromJson(String json) { 190 return Jsonable.fromJson(json); 191 } 192 193 /** 194 * Entrypoint for constructing an instance of this class. 195 * 196 * @return A new Builder. 197 * @since 7.3.0 198 */ 199 public static Builder builder() { 200 return new Builder(); 201 } 202 203 /** 204 * Builder for constructing a Call object. 205 * 206 * @since 7.3.0 207 */ 208 public static class Builder { 209 private Endpoint[] to; 210 private Endpoint from; 211 private HttpMethod answerMethod, eventMethod; 212 private String answerUrl, eventUrl; 213 private MachineDetection machineDetection; 214 private AdvancedMachineDetection advancedMachineDetection; 215 private Integer lengthTimer, ringingTimer; 216 private Boolean fromRandomNumber; 217 private Collection<Action> ncco; 218 219 Builder() {} 220 221 /** 222 * Sets the endpoints (recipients) of the call. 223 * 224 * @param endpoints The recipients of the call in order. 225 * 226 * @return This builder. 227 */ 228 public Builder to(Endpoint... endpoints) { 229 to = endpoints; 230 return this; 231 } 232 233 /** 234 * Connect to a Phone (PSTN) number. 235 * 236 * @param number The number to place the call from in E.164 format. 237 * 238 * @return This builder. 239 */ 240 public Builder from(String number) { 241 return from(new PhoneEndpoint(number)); 242 } 243 244 /** 245 * Sets the outbound caller. 246 * 247 * @param from The caller endpoint. 248 * 249 * @return This builder. 250 */ 251 Builder from(Endpoint from) { 252 this.from = from; 253 return this; 254 } 255 256 /** 257 * The HTTP method used to send event information to {@code event_url}. 258 * 259 * @param eventMethod The method type (must be {@code GET} or {@code POST}). 260 * 261 * @return This builder. 262 */ 263 public Builder eventMethod(HttpMethod eventMethod) { 264 this.eventMethod = eventMethod; 265 return this; 266 } 267 268 /** 269 * The HTTP method used to send event information to {@code answer_url}. 270 * 271 * @param answerMethod The method type (must be {@code GET} or {@code POST}). 272 * 273 * @return This builder. 274 */ 275 public Builder answerMethod(HttpMethod answerMethod) { 276 this.answerMethod = answerMethod; 277 return this; 278 } 279 280 /** 281 * The webhook endpoint where call progress events are sent to. 282 * For more information about the values sent, see the 283 * <a href=https://developer.vonage.com/en/voice/voice-api/webhook-reference#event-webhook> 284 * Event webhook documentation</a>. 285 * 286 * @param eventUrl The event updates URL. 287 * 288 * @return This builder. 289 */ 290 public Builder eventUrl(String eventUrl) { 291 this.eventUrl = eventUrl; 292 return this; 293 } 294 295 /** 296 * The webhook endpoint where you provide the Nexmo Call Control Object that governs this call. 297 * 298 * @param answerUrl The NCCO answer URL. 299 * 300 * @return This builder. 301 */ 302 public Builder answerUrl(String answerUrl) { 303 this.answerUrl = answerUrl; 304 return this; 305 } 306 307 /** 308 * Configure the behavior when Vonage detects that the call is answered by voicemail. If 309 * {@linkplain MachineDetection#CONTINUE}, Vonage sends an HTTP request to {@code event_url} with the Call 310 * event machine. If {@linkplain MachineDetection#HANGUP}, Vonage ends the call. 311 * 312 * @param machineDetection The machine detection mode. 313 * 314 * @return This builder. 315 */ 316 public Builder machineDetection(MachineDetection machineDetection) { 317 this.machineDetection = machineDetection; 318 return this; 319 } 320 321 /** 322 * Configure the behavior of Vonage's advanced machine detection. This overrides the 323 * {@link #machineDetection(MachineDetection)}, so you cannot set both. 324 * 325 * @param advancedMachineDetection The advanced machine detection settings. 326 * 327 * @return This builder. 328 * 329 * @since 7.4.0 330 */ 331 public Builder advancedMachineDetection(AdvancedMachineDetection advancedMachineDetection) { 332 this.advancedMachineDetection = advancedMachineDetection; 333 return this; 334 } 335 336 /** 337 * Sets the number of seconds that elapse before Vonage hangs up after the call is answered. 338 * 339 * @param lengthTimer The call length in seconds. The default and maximum is 7200. 340 * 341 * @return This builder. 342 */ 343 public Builder lengthTimer(int lengthTimer) { 344 this.lengthTimer = lengthTimer; 345 return this; 346 } 347 348 /** 349 * Sets the number of seconds that elapse before Vonage hangs up after the call state changes to ‘ringing’. 350 * 351 * @param ringingTimer The time to wait whilst ringing in seconds. Maximum is 120, default is 60. 352 * 353 * @return This builder. 354 */ 355 public Builder ringingTimer(int ringingTimer) { 356 this.ringingTimer = ringingTimer; 357 return this; 358 } 359 360 /** 361 * Set to @{code true} to use random phone number as the caller. The number will be selected from the list 362 * of the numbers assigned to the current application. Cannot be used if {@link #from(String)} is set. 363 * 364 * @param fromRandomNumber Whether to use a random number instead of {@code from}. 365 * 366 * @return This builder. 367 */ 368 public Builder fromRandomNumber(boolean fromRandomNumber) { 369 this.fromRandomNumber = fromRandomNumber; 370 return this; 371 } 372 373 /** 374 * Sets the actions to take on the call. 375 * 376 * @param actions The actions in order. 377 * 378 * @return This builder. 379 */ 380 public Builder ncco(Action... actions) { 381 return ncco(Arrays.asList(actions)); 382 } 383 384 /** 385 * Sets the actions to take on the call. 386 * 387 * @param nccos The NCCOs to use for this call. 388 * 389 * @return This builder. 390 */ 391 public Builder ncco(Collection<? extends Action> nccos) { 392 ncco = new ArrayList<>(nccos); 393 return this; 394 } 395 396 /** 397 * Builds the Call object with this builder's properties. 398 * 399 * @return The constructed Call instance. 400 */ 401 public Call build() { 402 return new Call(this); 403 } 404 } 405}