UAS API - Outbound Connect Sample Application
-
Filename:
samples/outbound_connect.py
Description:
An outbound application that makes calls to two destination addresses and connects them together. The destination addresses are supplied in the
outbound_parameters
variable, using the semi-colon (;) as a delimiter. This application also requires one extra channel.First the two call destination addresses are extracted from
outbound_parameters
using Python'ssplit()
function. Then the extra channel is retrieved from thechannel.ExtraChannel
list. Extra channels are set on the Inbound and Outbound Services pages.The application places two calls and the calling code has been put in a function called
_place_call
, which takes a channel, destination address andcall_from
as parameters. Thecall_from
variable identifies the calling party and its value is taken fromapplication_parameters
which is configured on the Outbound Services page.The first outbound call is placed and, if successful, TTS is used to ask the user to speak a message after the beep. To create a beep, a DTMF tone is played using the
channel.DTMFPlayer.play()
function. The message is recorded and then the caller is asked to hold while the system calls the second destination..The extra channel is used to call the second destination and the recorded message from the first call is played using the
channel.FilePlayer.start()
function which does not block. This allows the application to use TTS to tell the first caller that his message is being played.Once the message has been played, the application joins the two calls together using the
channel.connect()
function, this allows the two calls to communicate with each other. The application then waits for either party to hang up, using thechannel.state()
function on each channel.Code:
""" An outbound application that makes calls to two destination addresses (passed in through outbound_parameters using the semi-colon as a delimiter). It prompts the first call to record a message and plays this to the second call before connecting the two calls together. It then waits for the calls to be hung up. This application requires that call_from is supplied in application_parameters and it requires one extra channel (this is configured on the outbound service page of the CWP). For information on placing outbound calls please read the online web services documentation. For additional information regarding outbound PSTN calls, please see the online documentation. Also have a look at the invoke_outbound_service sample code in the samples_ws directory. Actions: - place an outbound call - record a message - place another outbound call - play the message to the outbound call - connect the two calls together - wait for someone to hang up """ from prosody.uas import Hangup, Error, ICanRun import time __uas_version__ = "0.0.1" __uas_identify__ = "application" def _place_call(channel, destination, call_from): # Place an outbound call. If the destination is BUSY, # wait ten seconds and try again; try for one minute. endTime = time.time() + 60 while channel.call(destination, call_from=call_from) != channel.State.ANSWERED: cause = channel.cause() if cause != channel.Cause.BUSY: raise Error("Call destination returned cause {0}".format(cause)) if time.time() > endTime: raise Hangup("Call destination is busy.") time.sleep(10) continue def main(channel, application_instance_id, file_man, my_log, application_parameters, outbound_parameters): try: return_code = 0 out_channel_2 = None # read the outbound args, we're expecting two destination_address with a ; as delimiter my_log.info("Out Args: {0}".format(outbound_parameters)) out_arg_splits = outbound_parameters.split(';') try: outbound_destination_1 = out_arg_splits[0] outbound_destination_2 = out_arg_splits[1] # we expect the call_from details to be in application_parameters # this is used in CLI validation for PSTN calls call_from = application_parameters except: raise Error("Could not get outbound call destinations.") # get the extra channel try: out_channel_2 = channel.ExtraChannel[0] except: raise Error("You need to register an extra channel for this application") #place the first call _place_call(channel, outbound_destination_1, call_from) my_log.info("Placed first outbound call") # prompt the party to record a message, the prompt is played using TTS cause = channel.FilePlayer.say("<acu-engine name='Polly'><voice name='Brian'>Hello, please record a message after the beep.</voice></acu-engine>") if cause != channel.FilePlayer.Cause.NORMAL: raise Error("Say hello failed: cause is {0}".format(cause)) # log at error level # play a DTMF digit for the tone cause = channel.DTMFPlayer.play('#') if cause != channel.DTMFPlayer.Cause.NORMAL: raise Error("Play # failed: cause is {0}".format(cause)) # record the message, record until we've had three seconds of silence cause = channel.FileRecorder.record('recorded_message_{0}.wav'.format(application_instance_id), milliseconds_max_silence=3000) if cause != channel.FileRecorder.Cause.SILENCE: raise Error("Record message failed: cause is {0}".format(cause)) # log at error level # thanks for the recording cause = channel.FilePlayer.say("Thank you, please wait while I phone a friend.") if cause != channel.FilePlayer.Cause.NORMAL: raise Error("Say thank you failed: cause is {0}".format(cause)) # log at error level # place next call _place_call(out_channel_2, outbound_destination_2, call_from) # We have an outbound call, start playing the recording to the outbound call if out_channel_2.FilePlayer.start('recorded_message_{0}.wav'.format(application_instance_id)) is False: raise Error("Failed to start playing recording to outbound channel") # tell the inbound channel what we're doing cause = channel.FilePlayer.say("I am playing your message to your friend.") if cause != channel.FilePlayer.Cause.NORMAL: raise Error("Say playing to friend failed: cause is {0}".format(cause)) # wait for message play to complete cause = out_channel_2.FilePlayer.wait_until_finished() if cause != out_channel_2.FilePlayer.Cause.NORMAL: raise Error("Wait for finished failed: cause is {0}".format(cause)) # indicate to the far end that the message has finished playing cause = out_channel_2.DTMFPlayer.play('#') if cause != out_channel_2.DTMFPlayer.Cause.NORMAL: raise Error("DTMF play returned {0}: expected {1}".format(cause, out_channel_2.DTMFPlayer.Cause.NORMAL)) # let the parties talk to each other if channel.connect(out_channel_2) is True: # wait until either call hangs up # wait on i_can_run to prevent an accidental infinite loop i_can_run = ICanRun(channel) while i_can_run: # throw hangup exception on IDLE channel.state(check_for_hangup=True) out_channel_2.state(check_for_hangup=True) time.sleep(1) else: raise Error('Connect channels failed') except Hangup as exc: my_log.info("Hangup exception reports: {0}".format(exc)) # in this app a hangup is not an error, return a positive value return_code = 100 except Error as exc: # for error conditions return a negative value my_log.error("Error exception reports: {0}".format(exc)) return_code = -101 except Exception as exc: # an unexpected exception, return a negative value my_log.exception("Unexpected exception reports: {0}".format(exc)) return_code = -102 finally: if channel is not None: if channel.state() != channel.State.IDLE: channel.hang_up() if out_channel_2 is not None: if out_channel_2.state() != out_channel_2.State.IDLE: out_channel_2.hang_up() return return_code
-
Filename:
Samples\C#\OutboundConnect\OutboundConnect.cs
Description:
In this application we extract two call destination addresses from the
outboundParameters
argument using theString.Split()
method and then ensure we have at least one extra channel available. This will allow us to make one outbound call on the primary channel (thechannel
argument) and one outbound call on an extra channel.If either of the destination addresses are PSTN numbers then the source address (caller Id) that is passed into the application via the
applicationParameters
argument must be set to a PSTN number that has been validated from the Caller Id page on cloud.aculab.com.We call the first destination address on the primary channel using the
channel.Call()
method and when answered, we use TTS to ask the callee to record a message that will ultimately be played to the second callee. We can record a message using thechannel.FileRecorder.Start()
method. This requires a filename as a parameter to identify where to store the recorded data. To play a message prompt, we use thechannel.DTmfPlayer.Play()
method. We wait for silence from the other end for a couple of seconds before ending the recording.We then call the second destination address on one of the extra channels and when answered, we play the previously recorded message to the second callee. Then we use TTS to notify both callees that they are being connected and use the
Connect()
method to connect both calls together bidirectionally. To wait for either caller to hangup, we use thechannel.WaitForIdle()
method.Code:
using System; using System.Threading; using AMSClassLibrary; using UASAppAPI; // An outbound application that makes calls to two destination addresses (passed in). // It prompts the first call to record a message and plays this to the second call // before tromboning the two calls together. It then waits for the calls to be hung up. // // The Caller Id (callFrom) that is to be used when calling out should be supplied in // the applicationsParameters field. For outgoing SIP calls this can be empty, but for // outgoing PSTN calls see the Outbound Calls documentation for your platform. // // Requires: // [outboundParameters = two destination addresses for the two outgoing calls, semicolon delimited] // [applicationParameters = an optional validated Caller Id] // [1 extra channels] // // e.g. // outboundParameters = "sip:bob@bobscompany.com;sip:brian@brianscompany.com" // applicationParameters = "" // or // outboundParameters = "441908081876;441908678180" // applicationParameters = "441908876543" // namespace OutboundConnect { // The application class. // This must have the same name as the assembly and must inherit from either // UASInboundApplication or UASOutboundApplication. // It must override the Run method. public class OutboundConnect : UASOutboundApplication { // Possible return codes enum ReturnCode { // Success Codes: Success = 0, // ... any positive integer // Fail Codes: // -1 to -99 reserved ExceptionThrown = -100, ArgumentError = -101, NoCallerIdError = -102, NotSufficientExtraChannels = -103 } // This is the entry point for the application public override int Run(UASCallChannel channel, string applicationParameters, // Caller Id (may be required for outgoing PSTN, optional for outgoing SIP) string outboundParameters) // 2 x destinationAddresses, semicolon delimited... { this.Trace.TraceInfo("Start - appParms [{0}] outParms [{1}]", applicationParameters, outboundParameters); ReturnCode reply = ReturnCode.Success; UASCallChannel otherChannel = null; string recFileName = "recordings/introMessage"; try { // Typically the outboundParameters field is simply the destination address. // However, in this case we require 2 destination addresses to be specified. string[] destinationAddress = outboundParameters.Split(';'); if (destinationAddress.Length < 2) { reply = ReturnCode.ArgumentError; this.Trace.TraceError("This app requires 2 destination addresses in the outboundParameters field"); } // Check we've got an extra Channel if (reply == ReturnCode.Success) { if (channel.ExtraChannels.Length < 1) { this.Trace.TraceError("This app requires at least 1 extra channel to be configured"); reply = ReturnCode.NotSufficientExtraChannels; } } // Make the first call on the primary channel if (reply == ReturnCode.Success) { string callFrom = applicationParameters; CallState state = channel.Call(destinationAddress[0], callFrom); if (state == CallState.Answered) { // Record an introduction message channel.FilePlayer.Say("Hello." + "Please record an introduction message for " + destinationAddress[1] + "after the tone." + "Press any digit to stop the recording."); Thread.Sleep(1000); // Start recording, enabling barge in channel.FileRecorder.Start(recFileName, true); // Play a tone to signal that recording has started channel.DtmfPlayer.Play("0"); // Wait for a period for recording to complete (10 seconds of silence) // or the call to have been hung up FileRecorderCause recCause = channel.FileRecorder.WaitUntilFinished(60); if (recCause == FileRecorderCause.Timeout) { channel.FileRecorder.Stop(); } // And play it back if (channel.State != CallState.Idle) { // Replay the recorded message channel.FilePlayer.Say("The following message has been recorded"); channel.FilePlayer.Play(recFileName); } } } // Make the second call on the extra channel if (reply == ReturnCode.Success) { channel.FilePlayer.Say("Please wait while phoning contact."); string callFrom = applicationParameters; otherChannel = channel.ExtraChannels[0]; if (otherChannel.Call(destinationAddress[1], callFrom) == CallState.Answered) { // Play the introductory message to other call otherChannel.FilePlayer.Play(recFileName); // Connect the calls together channel.FilePlayer.Say("Connecting call"); otherChannel.FilePlayer.Say("Connecting call"); channel.Connect(otherChannel); } // Wait for hang up channel.WaitForIdle(Timeout.Infinite); } } catch (Exception e) { this.Trace.TraceError("Exception thrown {0}", e.Message); reply = ReturnCode.ExceptionThrown; } finally { channel.HangUp(); if (otherChannel != null) { otherChannel.HangUp(); } } this.Trace.TraceInfo("Completed"); return (int)reply; } } }
-
Filename:
Samples\VB\OutboundConnect\OutboundConnect.vb
Description:
In this application we extract two call destination addresses from the
outboundParameters
argument using theString.Split()
method and then ensure we have at least one extra channel available. This will allow us to make one outbound call on the primary channel (thechannel
argument) and one outbound call on an extra channel.If either of the destination addresses are PSTN numbers then the source address (caller Id) that is passed into the application via the
applicationParameters
argument must be set to a PSTN number that has been validated from the Caller Id page on cloud.aculab.com.We call the first destination address on the primary channel using the
channel.Call()
method and when answered, we use TTS to ask the callee to record a message that will ultimately be played to the second callee. We can record a message using thechannel.FileRecorder.Start()
method. This requires a filename as a parameter to identify where to store the recorded data. To play a message prompt, we use thechannel.DTmfPlayer.Play()
method. We wait for silence from the other end for a couple of seconds before ending the recording.We then call the second destination address on one of the extra channels and when answered, we play the previously recorded message to the second callee. Then we use TTS to notify both callees that they are being connected and use the
Connect()
method to connect both calls together bidirectionally. To wait for either caller to hangup, we use thechannel.WaitForIdle()
method.Code:
Imports AMSClassLibrary Imports UASAppAPI ' An outbound application that makes calls to two destination addresses (passed in). ' It prompts the first call to record a message and plays this to the second call ' before tromboning the two calls together. It then waits for the calls to be hung up. ' ' The Caller Id (callFrom) that is to be used when calling out should be supplied in ' the applicationsParameters field. For outgoing SIP calls this can be empty, but for ' outgoing PSTN calls see the Outbound Calls documentation for your platform. ' ' Requires: ' [outboundParameters = two destination addresses for the two outgoing calls, semicolon delimited] ' [applicationParameters = a validated Caller Id] ' [1 extra channels] ' ' e.g. ' outboundParameters = "sip:bob@bobscompany.com;sip:brian@brianscompany.com" ' applicationParameters = "" ' or ' outboundParameters = "441908081876;441908678180" ' applicationParameters = "441908876543" ' Namespace OutboundConnect ' The application class. ' This must have the same name as the assembly and must inherit from either ' UASInboundApplication or UASOutboundApplication. ' It must override the Run method. Public Class OutboundConnect Inherits UASOutboundApplication ' Possible return codes Enum ReturnCode ' Success Codes: Success = 0 ' ... any positive integer ' Fail Codes: ' -1 to -99 reserved ExceptionThrown = -100 ArgumentError = -101 NoCallerIdError = -102 NotSufficientExtraChannels = -103 End Enum ' This is the entry point for the application ' applicationParameters - Caller Id (required for outgoing PSTN, optional for outgoing SIP) ' outboundParameters - 2 x destinationAddresses, semicolon delimited Overrides Function Run(ByVal channel As UASCallChannel, _ ByVal applicationParameters As String, _ ByVal outboundParameters As String) _ As Integer Me.Trace.TraceInfo("Start - appParms [{0}] outParms [{1}]", _ applicationParameters, _ outboundParameters) Dim reply As ReturnCode = ReturnCode.Success Dim otherChannel As UASCallChannel = Nothing Dim recFileName = "recordings/introMessage" Try ' Typically the outboundParameters field is simply the destination address. ' However, in this case we require 2 destination addresses to be specified. Dim destinationAddress = outboundParameters.Split(";") If destinationAddress.Length < 2 Then reply = ReturnCode.ArgumentError Me.Trace.TraceError("This app requires 2 destination addresses in the outboundParameters field") End If ' Check we've got an extra Channel If reply = ReturnCode.Success Then If channel.ExtraChannels.Length < 1 Then Me.Trace.TraceError("This app requires at least 1 extra channel to be configured") reply = ReturnCode.NotSufficientExtraChannels End If End If ' Check we've got a Caller Id supplied if either of these calls are PSTN If reply = ReturnCode.Success Then If Not (destinationAddress(0).Contains("@") And destinationAddress(1).Contains("@")) Then ' At least one address is a PSTN number If String.IsNullOrEmpty(applicationParameters) Then Me.Trace.TraceError("This app requires a validated CallerId in the applicationParameters" + _ "if any PSTN calls are to be made") reply = ReturnCode.NoCallerIdError End If End If End If ' Make the first call on the primary channel If reply = ReturnCode.Success Then Dim callFrom = applicationParameters Dim state = channel.Call(destinationAddress(0), callFrom) If state = CallState.Answered Then ' Record an introduction message channel.FilePlayer.Say("Hello." + _ "Please record an introduction message for " + _ destinationAddress(1) + _ "after the tone." + _ "Press any digit to stop the recording.") Thread.Sleep(1000) ' Start recording, enabling barge in channel.FileRecorder.Start(recFileName, True) ' Play a tone to signal that recording has started channel.DtmfPlayer.Play("0") ' Wait for a period for recording to complete or ' the call to have been hung up Dim recCause = channel.FileRecorder.WaitUntilFinished(30) If recCause = FileRecorderCause.Timeout Then channel.FileRecorder.Stop() End If ' And play it back If channel.State <> CallState.Idle Then ' Replay the recorded message channel.FilePlayer.Say("The following message has been recorded") channel.FilePlayer.Play(recFileName) End If End If End If ' Make the second call on the extra channel If reply = ReturnCode.Success Then channel.FilePlayer.Say("Please wait while phoning contact.") Dim callFrom = applicationParameters otherChannel = channel.ExtraChannels(0) If otherChannel.Call(destinationAddress(1), callFrom) = CallState.Answered Then ' Play the introductory message to other call otherChannel.FilePlayer.Play(recFileName) ' Connect the calls together channel.FilePlayer.Say("Connecting call") otherChannel.FilePlayer.Say("Connecting call") channel.Connect(otherChannel) End If ' Wait for hang up channel.WaitForIdle(Timeout.Infinite) End If Catch ex As Exception Me.Trace.TraceError("Exception thrown {0}", ex.Message) reply = ReturnCode.ExceptionThrown Finally channel.HangUp() End Try Me.Trace.TraceInfo("Completed") Return reply End Function End Class End Namespace
-
Filename:
Samples\F#\OutboundConnect\OutboundConnect.fs
Description:
In this application we first ensure we have at least one extra channel available. Then we extract two call destination addresses from the
outboundParameters
argument using theString.Split()
method. This will allow us to make one outbound call on the primary channel (thechannel
argument) and one outbound call on an extra channel.If either of the destination addresses are PSTN numbers then the source address (caller Id) that is passed into the application via the
applicationParameters
argument must be set to a PSTN number that has been validated from the Caller Id page on cloud.aculab.com. You can set this in theApplication Parameters
field of the Outbound Service that runs this application. In the application this is checked using the recursive member functionContainsPSTNAddress()
.We call the first destination address on the primary channel using the
channel.Call()
method and when answered, we use TTS to ask the callee to record a message that will ultimately be played to the second callee. We can record a message using thechannel.FileRecorder.Start()
method. This requires a filename as a parameter to identify where to store the recorded data. To play a message prompt, we use thechannel.DTmfPlayer.Play()
method. We wait for silence from the other end for a couple of seconds before ending the recording.We then call the second destination address on one of the extra channels and when answered, we play the previously recorded message to the second callee. Then we use TTS to notify both callees that they are being connected and use the
Connect()
method to connect both calls together bidirectionally. To wait for either caller to hangup, we use thechannel.WaitForIdle()
method.Code:
// An outbound application that makes calls to two destination addresses (passed in). // It prompts the first call to record a message and plays this to the second call // before tromboning the two calls together. It then waits for the calls to be hung up. // // The Caller Id (callFrom) that is to be used when calling out should be supplied in // the applicationsParameters field. For outgoing SIP calls this can be empty, but for // outgoing PSTN calls see the Outbound Calls documentation for your platform. // // Requires: // [outboundParameters = two destination addresses for the two outgoing calls, semicolon delimited] // [applicationParameters = a validated Caller Id] // [1 extra channels] namespace OutboundConnect open System open System.Threading open AMSClassLibrary open UASAppAPI // Possible return codes type ReturnCode = // Success Codes: | Success = 0 // ... any positive integer // Fail Codes: // -1 to -99 reserved | ExceptionThrown = -100 | ArgumentError = -101 | NoCallerIdError = -102 | NotSufficientExtraChannels = -103 // The application class. // This must have the same name as the assembly and must inherit from either // UASInboundApplication or UASOutboundApplication. // It must override the Run method. type OutboundConnect() = inherit UASOutboundApplication() // This is the entry point for the application override obj.Run(channel:UASCallChannel, applicationParameters:string, outboundParameters:string) : int = obj.Trace.TraceInfo("Started") let mutable reply = ReturnCode.Success // Check we've got an extra Channel if channel.ExtraChannels.Length < 1 then obj.Trace.TraceError("This app requires at least 1 extra channel to be configured") int ReturnCode.NotSufficientExtraChannels else let otherChannel : UASCallChannel = channel.ExtraChannels.[0] let recFileName = "recordings/introMessage" try try // Typically the outboundParameters field is simply the destination address. // However, in this case we require 2 destination addresses to be specified. let destinationAddresses = outboundParameters.Split(';') if destinationAddresses.Length < 2 then reply <- ReturnCode.ArgumentError obj.Trace.TraceError("This app requires 2 destination addresses in the outboundParameters field") // Check we've got a Caller Id supplied if either of these calls are PSTN if reply = ReturnCode.Success then if obj.ContainsPSTNAddress(destinationAddresses) then // At least one address is a PSTN number if String.IsNullOrEmpty(applicationParameters) then obj.Trace.TraceError("This app requires a validated CallerId in the applicationParameters" + "if any PSTN calls are to be made") reply <- ReturnCode.NoCallerIdError // Make the first call on the primary channel if reply = ReturnCode.Success then let callFrom = applicationParameters let state = channel.Call(destinationAddresses.[0], callFrom) if state = CallState.Answered then // Record an introduction message channel.FilePlayer.Say("Hello." + "Please record an introduction message for " + destinationAddresses.[1] + "after the tone." + "Press any digit to stop the recording.") |> ignore Thread.Sleep(1000) // Start recording, enabling barge in channel.FileRecorder.Start(recFileName, true) |> ignore // Play a tone to signal that recording has started channel.DtmfPlayer.Play("0") |> ignore // Wait for a period for recording to complete or // the call to have been hung up let recCause = channel.FileRecorder.WaitUntilFinished(30) if recCause = FileRecorderCause.Timeout then channel.FileRecorder.Stop() |> ignore // And play it back if channel.State <> CallState.Idle then // Replay the recorded message channel.FilePlayer.Say("The following message has been recorded") |> ignore channel.FilePlayer.Play(recFileName) |> ignore // Make the second call on the extra channel if reply = ReturnCode.Success then channel.FilePlayer.Say("Please wait while phoning contact.") |> ignore let callFrom = applicationParameters if otherChannel.Call(destinationAddresses.[1], callFrom) = CallState.Answered then // Play the introductory message to other call otherChannel.FilePlayer.Play(recFileName) |> ignore // Connect the calls together channel.FilePlayer.Say("Connecting call") |> ignore otherChannel.FilePlayer.Say("Connecting call") |> ignore channel.Connect(otherChannel) |> ignore // Wait for hang up channel.WaitForIdle(Timeout.Infinite) |> ignore with | _ as e-> obj.Trace.TraceError("Exception thrown {0}", e.Message) reply <- ReturnCode.ExceptionThrown finally channel.HangUp() |> ignore if otherChannel <> null then otherChannel.HangUp() |> ignore obj.Trace.TraceInfo("Completed") int reply // Check whether an array of addresses contains a PSTN address member obj.ContainsPSTNAddress (addresses : string[]) = let rec IsPSTNAddress(addresses : string[], index : int) : bool = if index < addresses.Length then false elif addresses.[index].Contains("@") then true else IsPSTNAddress(addresses, index + 1) IsPSTNAddress(addresses, 0)