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 com.vonage.client.voice.AdvancedMachineDetection;
021import com.vonage.client.voice.MachineDetection;
022import java.net.URI;
023import java.util.Arrays;
024import java.util.Collection;
025
026/**
027 * An NCCO connect action that allows for the establishment of a connection to various {@link Endpoint}.
028 */
029public class ConnectAction extends JsonableBaseObject implements Action {
030    private static final String ACTION = "connect";
031
032    private Collection<Endpoint> endpoint;
033    private String from;
034    private EventType eventType;
035    private Integer limit, timeOut;
036    private MachineDetection machineDetection;
037    private AdvancedMachineDetection advancedMachineDetection;
038    private Collection<String> eventUrl;
039    private EventMethod eventMethod;
040    private Boolean randomFromNumber;
041    private URI ringbackTone;
042
043    ConnectAction() {}
044
045    private ConnectAction(Builder builder) {
046        if ((endpoint = builder.endpoint) == null || endpoint.isEmpty()) {
047            throw new IllegalStateException("An endpoint must be specified.");
048        }
049        if ((limit = builder.limit) != null && (limit < 1 || limit > 7200)) {
050            throw new IllegalArgumentException("'limit' must be positive and less than 7200 seconds.");
051        }
052        if ((timeOut = builder.timeOut) != null && (timeOut < 3 || timeOut > 7200)) {
053            throw new IllegalArgumentException("'timeOut' must be between 3 and 7200 seconds.");
054        }
055        from = builder.from;
056        if ((randomFromNumber = builder.randomFromNumber) != null && from != null) {
057            throw new IllegalStateException("'randomFromNumber' and 'from' cannot be used together.");
058        }
059        eventType = builder.eventType;
060        machineDetection = builder.machineDetection;
061        advancedMachineDetection = builder.advancedMachineDetection;
062        eventUrl = builder.eventUrl;
063        eventMethod = builder.eventMethod;
064        ringbackTone = builder.ringbackTone;
065    }
066
067    @JsonProperty("action")
068    @Override
069    public String getAction() {
070        return ACTION;
071    }
072
073    @JsonProperty("endpoint")
074    public Collection<Endpoint> getEndpoint() {
075        return endpoint;
076    }
077
078    @JsonProperty("from")
079    public String getFrom() {
080        return from;
081    }
082
083    @JsonProperty("eventType")
084    public EventType getEventType() {
085        return eventType;
086    }
087
088    @JsonProperty("timeout")
089    public Integer getTimeOut() {
090        return timeOut;
091    }
092
093    @JsonProperty("limit")
094    public Integer getLimit() {
095        return limit;
096    }
097
098    @JsonProperty("machineDetection")
099    public MachineDetection getMachineDetection() {
100        return machineDetection;
101    }
102
103    @JsonProperty("advancedMachineDetection")
104    public AdvancedMachineDetection getAdvancedMachineDetection() {
105        return advancedMachineDetection;
106    }
107
108    @JsonProperty("eventUrl")
109    public Collection<String> getEventUrl() {
110        return eventUrl;
111    }
112
113    @JsonProperty("eventMethod")
114    public EventMethod getEventMethod() {
115        return eventMethod;
116    }
117
118    @JsonProperty("randomFromNumber")
119    public Boolean getRandomFromNumber() {
120        return randomFromNumber;
121    }
122
123    @JsonProperty("ringbackTone")
124    public URI getRingbackTone() {
125        return ringbackTone;
126    }
127
128    /**
129     * Entry point for constructing an instance of this class.
130     *
131     * @param endpoint Connect the call to a specific #{@link Endpoint}.
132     *
133     * @return A new Builder.
134     */
135    public static Builder builder(Collection<Endpoint> endpoint) {
136        return new Builder(endpoint);
137    }
138
139    /**
140     * Entry point for constructing an instance of this class.
141     *
142     * @param endpoint Connect the call to a specific {@linkplain Endpoint}.
143     *
144     * @return A new Builder.
145     */
146    public static Builder builder(Endpoint... endpoint) {
147        return builder(Arrays.asList(endpoint));
148    }
149
150    public static class Builder {
151        private Collection<Endpoint> endpoint;
152        private String from;
153        private EventType eventType;
154        private Integer timeOut, limit;
155        private MachineDetection machineDetection;
156        private AdvancedMachineDetection advancedMachineDetection;
157        private Collection<String> eventUrl;
158        private EventMethod eventMethod;
159        private Boolean randomFromNumber;
160        private URI ringbackTone;
161
162        Builder(Collection<Endpoint> endpoint) {
163            this.endpoint = endpoint;
164        }
165
166        /**
167         * Connect the call to a specific {@linkplain Endpoint}.
168         *
169         * @param endpoint The endpoints to connect to.
170         *
171         * @return This builder.
172         * @deprecated This will be removed in the next major release.
173         */
174        @Deprecated
175        public Builder endpoint(Collection<Endpoint> endpoint) {
176            this.endpoint = endpoint;
177            return this;
178        }
179
180        /**
181         * Connect the call to a specific {@linkplain Endpoint}.
182         *
183         * @param endpoint The endpoint(s) to connect to.
184         *
185         * @return This builder.
186         */
187        public Builder endpoint(Endpoint... endpoint) {
188            return endpoint(Arrays.asList(endpoint));
189        }
190
191        /**
192         * Sets the caller ID number. This must be one of your Vonage virtual numbers.
193         * Any other value will result in the caller ID being unknown.
194         *
195         * @param from The caller number in <a href="https://en.wikipedia.org/wiki/E.164">E.164 format</a>.
196         *
197         * @return This builder.
198         */
199        public Builder from(String from) {
200            this.from = from;
201            return this;
202        }
203
204        /**
205         * Set to {@link EventType#SYNCHRONOUS} to:
206         * <ul>
207         * <li>Make the connect action synchronous.
208         * <li>Enable eventUrl to return an NCCO that overrides the current NCCO when a call moves to
209         * specific states.
210         * </ul>
211         * <p>
212         * See the <a href="https://developer.vonage.com/voice/voice-api/ncco-reference#connect-with-fallback-ncco">Connect with fallback NCCO example.</a>
213         *
214         * @param eventType The event type as an enum.
215         *
216         * @return This builder.
217         */
218        public Builder eventType(EventType eventType) {
219            this.eventType = eventType;
220            return this;
221        }
222
223        /**
224         * If the call is unanswered, set the number in seconds before Vonage stops ringing endpoint.
225         * The default value is 60, minimum is 3 and maximum is 7200 (2 hours).
226         *
227         * @param timeOut The call timeout in seconds.
228         *
229         * @return This builder.
230         */
231        public Builder timeOut(Integer timeOut) {
232            this.timeOut = timeOut;
233            return this;
234        }
235
236        /**
237         * Maximum length of the call in seconds. The default and maximum value is 7200 seconds (2 hours).
238         *
239         * @param limit The maximum call length as an int.
240         *
241         * @return This builder.
242         */
243        public Builder limit(Integer limit) {
244            this.limit = limit;
245            return this;
246        }
247
248        /**
249         * Configure the behavior when Vonage detects that a destination is an answerphone.
250         *
251         * @param machineDetection
252         *                         Set to either:
253         *                         <ul>
254         *                         <li> {@link MachineDetection#CONTINUE} Vonage sends an HTTP request to event_url with the Call event machine
255         *                         <li> {@link MachineDetection#HANGUP} to end the Call
256         *                         </ul>
257         *
258         * @return This builder.
259         */
260        public Builder machineDetection(MachineDetection machineDetection) {
261            this.machineDetection = machineDetection;
262            return this;
263        }
264
265        /**
266         * Configure the behavior of Vonage's advanced machine detection. This overrides the
267         * {@link #machineDetection(MachineDetection)}, so you cannot set both.
268         *
269         * @param advancedMachineDetection The advanced machine detection settings.
270         *
271         * @return This builder.
272         *
273         * @since 7.4.0
274         */
275        public Builder advancedMachineDetection(AdvancedMachineDetection advancedMachineDetection) {
276            this.advancedMachineDetection = advancedMachineDetection;
277            return this;
278        }
279
280        /**
281         * Set the webhook endpoint that Vonage calls asynchronously on each of the possible
282         * <a href="https://developer.nexmo.com/voice/voice-api/guides/call-flow#call-states">Call States</a>.
283         * If eventType is set to synchronous the eventUrl can return an NCCO that overrides the current
284         * NCCO when a timeout occurs.
285         *
286         * @param eventUrl The event URLs.
287         *
288         * @return This builder.
289         */
290        public Builder eventUrl(Collection<String> eventUrl) {
291            this.eventUrl = eventUrl;
292            return this;
293        }
294
295        /**
296         * Set the webhook endpoint that Vonage calls asynchronously on each of the possible
297         * <a href="https://developer.nexmo.com/voice/voice-api/guides/call-flow#call-states">Call States</a>.
298         * If eventType is set to synchronous the eventUrl can return an NCCO that overrides the current
299         * NCCO when a timeout occurs.
300         *
301         * @param eventUrl The event URL(s).
302         *
303         * @return This builder.
304         */
305        public Builder eventUrl(String... eventUrl) {
306            return eventUrl(Arrays.asList(eventUrl));
307        }
308
309        /**
310         * The HTTP method Vonage uses to make the request to eventUrl.
311         * The default value is {@linkplain EventMethod#POST}.
312         *
313         * @param eventMethod The HTTP method as an enum.
314         *
315         * @return This builder.
316         */
317        public Builder eventMethod(EventMethod eventMethod) {
318            this.eventMethod = eventMethod;
319            return this;
320        }
321
322        /**
323         * Use a random phone number as {@code from}. The number will be selected from the list of the
324         * numbers assigned to the current application. The application will try to use number(s) from the
325         * same country as the destination (if available). If set to {@code true}, cannot be used together
326         * with {@linkplain #from(String)}. The default value is {@code false}.
327         *
328         * @param randomFromNumber {@code true} to use a random number instead of {@linkplain #from(String)}.
329         *
330         * @return This builder.
331         * @since 8.2.0
332         */
333        public Builder randomFromNumber(boolean randomFromNumber) {
334            this.randomFromNumber = randomFromNumber;
335            return this;
336        }
337
338        /**
339         * A URL value that points to a ringback tone to be played back on repeat to the caller, so they don't
340         * hear silence. The tone will automatically stop playing when the call is fully connected. It's not
341         * recommended to use this parameter when connecting to a phone endpoint, as the carrier will supply
342         * their own ringback tone.
343         *
344         * @param ringbackTone The ringback tone URL as a string.
345         *
346         * @return This builder.
347         * @since 8.2.0
348         */
349        public Builder ringbackTone(String ringbackTone) {
350            this.ringbackTone = URI.create(ringbackTone);
351            return this;
352        }
353
354        /**
355         * Builds the ConnectAction.
356         *
357         * @return A new {@link ConnectAction} object from the stored builder options.
358         */
359        public ConnectAction build() {
360            return new ConnectAction(this);
361        }
362    }
363}