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.ncco; 017 018import com.fasterxml.jackson.annotation.JsonProperty; 019import com.vonage.client.JsonableBaseObject; 020import java.util.*; 021import java.util.stream.Collectors; 022 023/** 024 * An NCCO conversation action which enables the ability to host conference calls. 025 */ 026public class ConversationAction extends JsonableBaseObject implements Action { 027 private static final String ACTION = "conversation"; 028 029 private String name; 030 private Boolean startOnEnter, endOnExit, record, mute; 031 private EventMethod eventMethod; 032 private Collection<String> musicOnHoldUrl, eventUrl, canSpeak, canHear; 033 private TranscriptionSettings transcription; 034 035 ConversationAction() {} 036 037 private ConversationAction(Builder builder) { 038 name = builder.name; 039 musicOnHoldUrl = builder.musicOnHoldUrl; 040 startOnEnter = builder.startOnEnter; 041 endOnExit = builder.endOnExit; 042 mute = builder.mute; 043 record = builder.record; 044 eventUrl = builder.eventUrl; 045 eventMethod = builder.eventMethod; 046 canSpeak = builder.canSpeak; 047 canHear = builder.canHear; 048 if ((transcription = builder.transcription) != null && (record == null || !record)) { 049 throw new IllegalStateException("Recording must be enabled for transcription."); 050 } 051 } 052 053 @Override 054 public String getAction() { 055 return ACTION; 056 } 057 058 @JsonProperty("name") 059 public String getName() { 060 return name; 061 } 062 063 @JsonProperty("musicOnHoldUrl") 064 public Collection<String> getMusicOnHoldUrl() { 065 return musicOnHoldUrl; 066 } 067 068 @JsonProperty("startOnEnter") 069 public Boolean getStartOnEnter() { 070 return startOnEnter; 071 } 072 073 @JsonProperty("endOnExit") 074 public Boolean getEndOnExit() { 075 return endOnExit; 076 } 077 078 @JsonProperty("mute") 079 public Boolean getMute() { 080 return mute; 081 } 082 083 @JsonProperty("record") 084 public Boolean getRecord() { 085 return record; 086 } 087 088 @JsonProperty("eventUrl") 089 public Collection<String> getEventUrl() { 090 return eventUrl; 091 } 092 093 @JsonProperty("eventMethod") 094 public EventMethod getEventMethod() { 095 return eventMethod; 096 } 097 098 @JsonProperty("canSpeak") 099 public Collection<String> getCanSpeak() { 100 return canSpeak; 101 } 102 103 @JsonProperty("canHear") 104 public Collection<String> getCanHear() { 105 return canHear; 106 } 107 108 @JsonProperty("transcription") 109 public TranscriptionSettings getTranscription() { 110 return transcription; 111 } 112 113 /** 114 * Entrypoint for constructing an instance of this class. 115 * 116 * @param name The name of the Conversation room. 117 * 118 * @return A new Builder. 119 */ 120 public static Builder builder(String name) { 121 return new Builder(name); 122 } 123 124 /** 125 * Builder for specifying the properties of a Conversation NCCO. 126 */ 127 public static class Builder { 128 private String name; 129 private EventMethod eventMethod; 130 private Boolean startOnEnter, endOnExit, record, mute; 131 private Collection<String> musicOnHoldUrl, eventUrl, canSpeak, canHear; 132 private TranscriptionSettings transcription; 133 134 Builder(String name) { 135 this.name = name; 136 } 137 138 /** 139 * Sets the name of the Conversation room. 140 * 141 * @param name The conversation name. 142 * 143 * @return This builder. 144 */ 145 public Builder name(String name) { 146 this.name = name; 147 return this; 148 } 149 150 /** 151 * @param musicOnHoldUrl A URL to the mp3 file to stream to participants until the conversation starts. 152 * By default, the conversation starts when the first person calls the virtual number 153 * associated with your Voice app. To stream this mp3 before the moderator joins the 154 * conversation, set startOnEnter to false for all users other than the moderator. 155 * 156 * @return This builder. 157 * @deprecated This will be removed in the next major release. 158 */ 159 @Deprecated 160 public Builder musicOnHoldUrl(Collection<String> musicOnHoldUrl) { 161 this.musicOnHoldUrl = musicOnHoldUrl; 162 return this; 163 } 164 165 /** 166 * A URL to the mp3 file to stream to participants until the conversation starts. 167 * By default, the conversation starts when the first person calls the virtual number 168 * associated with your Voice app. To stream this mp3 before the moderator joins the 169 * conversation, set startOnEnter to false for all users other than the moderator. 170 * 171 * @param musicOnHoldUrl Absolute URL to the hold music in MP3 format, as a string. 172 * 173 * @return This builder. 174 */ 175 public Builder musicOnHoldUrl(String... musicOnHoldUrl) { 176 return musicOnHoldUrl(Arrays.asList(musicOnHoldUrl)); 177 } 178 179 /** 180 * The default value of {@code true} ensures that the conversation starts when this caller joins 181 * the conversation. Set to false for attendees in a moderated conversation. 182 * 183 * @param startOnEnter Whether to start the conversation when joining. 184 * 185 * @return This builder. 186 */ 187 public Builder startOnEnter(Boolean startOnEnter) { 188 this.startOnEnter = startOnEnter; 189 return this; 190 } 191 192 /** 193 * For moderated conversations, set to {@code true} in the moderator NCCO so the conversation is ended 194 * when the moderator hangs up. The default value of false means the conversation is not terminated 195 * when a caller hangs up; the conversation ends when the last caller hangs up. 196 * 197 * @param endOnExit Whether to end the conversation when the moderator hangs up. 198 * 199 * @return This builder. 200 */ 201 public Builder endOnExit(Boolean endOnExit) { 202 this.endOnExit = endOnExit; 203 return this; 204 } 205 206 /** 207 * Set to {@code true} to record this conversation. For standard conversations, recordings start 208 * when one or more attendees connects to the conversation. For moderated conversations, recordings 209 * start when the moderator joins. That is, when an NCCO is executed for the named conversation where 210 * startOnEnter is set to true. When the recording is terminated, the URL you download the recording 211 * from is sent to the event URL. 212 * <p> 213 * By default, audio is recorded in MP3 format. See the 214 * <a href="https://developer.nexmo.com/voice/voice-api/guides/recordingfile-formats">recording guide</a> 215 * for more details. 216 * 217 * @param record Whether to enable recording. 218 * 219 * @return This builder. 220 */ 221 public Builder record(Boolean record) { 222 this.record = record; 223 return this; 224 } 225 226 /** 227 * @param eventUrl Set the URL to the webhook endpoint Vonage calls asynchronously on each of the 228 * <a href="https://developer.nexmo.com/voice/voice-api/guides/call-flowcall-states">Call States</a>. 229 * 230 * @return This builder. 231 * @deprecated This will be removed in the next major release. 232 */ 233 @Deprecated 234 public Builder eventUrl(Collection<String> eventUrl) { 235 this.eventUrl = eventUrl; 236 return this; 237 } 238 239 /** 240 * Set the URL to the webhook endpoint Vonage calls asynchronously on each of the 241 * <a href="https://developer.nexmo.com/voice/voice-api/guides/call-flowcall-states">Call States</a>. 242 * 243 * @param eventUrl The event URL as a string. 244 * 245 * @return This builder. 246 */ 247 public Builder eventUrl(String... eventUrl) { 248 return eventUrl(Arrays.asList(eventUrl)); 249 } 250 251 /** 252 * Set the HTTP method used to make the request to eventUrl. The default value is POST. 253 * 254 * @param eventMethod The event method as an enum. 255 * 256 * @return This builder. 257 */ 258 public Builder eventMethod(EventMethod eventMethod) { 259 this.eventMethod = eventMethod; 260 return this; 261 } 262 263 /** 264 * Whether to mute the participant. The audio from the participant will not be played to the 265 * conversation and will not be recorded. When using {@linkplain #canSpeak(Collection)}, the 266 * mute parameter is not supported. 267 * 268 * @param mute {@code true} to mute the participant. 269 * 270 * @return This builder. 271 * @since 8.2.0 272 */ 273 public Builder mute(boolean mute) { 274 this.mute = mute; 275 return this; 276 } 277 278 /** 279 * Convenience method for adding a leg ID to the {@code canSpeak} collection. 280 * The added leg ID will be able to hear this participant. 281 * 282 * @param uuid The participant leg ID to add as a string. 283 * 284 * @return This builder. 285 * @since 8.2.0 286 * @see #canSpeak(Collection) 287 */ 288 public Builder addCanSpeak(String... uuid) { 289 if (canSpeak == null) { 290 canSpeak = new LinkedHashSet<>(); 291 } 292 canSpeak.addAll(Arrays.asList(uuid)); 293 return this; 294 } 295 296 /** 297 * Convenience method for adding a leg ID to the {@code canHear} collection. 298 * This participant will be able to hear the participant associated with the provided leg ID. 299 * 300 * @param uuid The participant leg ID to add as a string. 301 * 302 * @return This builder. 303 * @since 8.2.0 304 */ 305 public Builder addCanHear(String... uuid) { 306 if (canHear == null) { 307 canHear = new LinkedHashSet<>(uuid.length); 308 } 309 canHear.addAll(Arrays.asList(uuid)); 310 return this; 311 } 312 313 /** 314 * A collection of leg UUIDs that this participant can be heard by. If not provided, the participant can 315 * be heard by everyone. If an empty collection is provided, the participant will not be heard by anyone. 316 * This will replace the current collection. 317 * 318 * @param canSpeak The leg UUIDs that can hear this participant speak. 319 * 320 * @return This builder. 321 * @since 8.2.0 322 * @see #addCanSpeak(String...) 323 */ 324 public Builder canSpeak(Collection<String> canSpeak) { 325 this.canSpeak = canSpeak; 326 return this; 327 } 328 329 /** 330 * A collection of leg UUIDs that this participant can hear. If not provided, the participant can hear 331 * everyone. If an empty collection is provided, the participant will not hear any other participants. 332 * This will replace the current collection. 333 * 334 * @param canHear The leg UUIDs that this participant can hear. 335 * 336 * @return This builder. 337 * @since 8.2.0 338 * @see #addCanHear(String...) 339 */ 340 public Builder canHear(Collection<String> canHear) { 341 this.canHear = canHear; 342 return this; 343 } 344 345 /** 346 * Transcription settings. If present (even if all settings are default), transcription is activated. 347 * The {@linkplain #record(Boolean)} parameter must be set to {@code true}. 348 * 349 * @param transcription The transcriptions settings. 350 * 351 * @return This builder. 352 * @since 8.2.0 353 */ 354 public Builder transcription(TranscriptionSettings transcription) { 355 this.transcription = transcription; 356 return this; 357 } 358 359 /** 360 * Builds the Conversation NCCO action. 361 * 362 * @return A new {@link ConversationAction} object from the stored builder options. 363 */ 364 public ConversationAction build() { 365 if (canSpeak != null) { 366 canSpeak = canSpeak.stream().distinct().collect(Collectors.toList()); 367 } 368 if (canHear != null) { 369 canHear = canHear.stream().distinct().collect(Collectors.toList()); 370 } 371 return new ConversationAction(this); 372 } 373 } 374}