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}