UAS API - Transfer Menu Sample Application
-
Filename:
samples/transfer_menu.py
Description:
An inbound application that prompts the user to select a target to be transfered to. The target names (to be read out using TTS) and addresses (to be transfered to using the channel API) are given in
application_parameters
which is configured on the Inbound Services page. The target addresses must be other inbound services that have been registered on the Inbound Services page.This example requires that
application_parameters
is a semi-colon delimited list in the format:<target name>,<target address>;<target name>,<target address>
for example:join a conference,sip:AddToConfService@sip-0-2-0.aculab.com;listen to music,sip:InBoundMusic@sip-0-2-0.aculab.com
This application requires one extra channel.The application rings and answers the call, it then tries to get the extra channel. It then splits up the information in
application_arguments
to generate the transfer menu list. The application then uses TTS to prompt the caller for a menu choice. Finally, it uses thechannel.transfer_to_inbound_service()
function to transfer the caller to another inbound service. This function requires a reference to an extra channel.The
get_menu_selection()
function has a loop which says each item in the transfer menu list, and then waits for a valid DTMF digit (one which corresponds to a transfer option). The transfer name and destination are returned by the function.Code:
__uas_identify__ = "application" __uas_version__ = "1.0b1" from prosody.uas import Hangup, Error, PlayableMedia from prosody.uas.highlevel import HighLevelCallChannel, HighLevelGrammar, HighLevelDigitInputOptions """ An inbound application that prompts the user to select a target to be transferred to. The target names and addresses - or phone numbers - are provided in application_parameters. These are set, during service registration, on the inbound service registration page at cloud.aculab.com. The application_parameters must be a delimited string in the format: <name>, <address>; <name>, <address> For example: "Fred,sip:fred@place.com;Jack,sip:jack@place.com" The target names are used to create an automatic speech recognition (ASR) grammar. ASR is used to get the caller's choice and the call is then connected to the target address. A digit is associated with each name, and this can be entered on the telephone keypad instead of speaking. At application start, the target names are read out along with the associated digit. This application requires 1 extra channel. Actions: - wait for an inbound call - generate the transfer options menu - speak the options using TTS - listen for the caller's selection using ASR and DTMF - transfer the call """ def transfer_menu(application_parameters, my_log): # Split the application_parameters up into transfer options # (split with semicolon), and then again with commas. Return the # data as a dictionary try: return dict(tuple(a.split(',')) for a in application_parameters.split(';')) except Exceptionas exc: my_log.error("The application parameters could not be parsed correctly: {0}".format(exc)) return {} def get_menu_selection(channel, transfer_list, my_log): channel.DTMFDetector.clear_digits() my_log.info("transfer_list: {0}".format(transfer_list)) # create a speech recognition grammar of the IVR options my_grammar = HighLevelGrammar() my_grammar.create_from_alternatives(*transfer_list.keys()) valid_digits = range(len(transfer_list)) my_digit_options = HighLevelDigitInputOptions(valid_digits=''.join(map(str, valid_digits))) prompt_text = 'Please say the name of the person you want to call. Or press ' for digit in valid_digits: # associate digits with the names prompt_text = prompt_text + "{0} for {1}, ".format(digit, transfer_list.keys()[digit]) prompt = PlayableMedia(text_to_say=prompt_text, channel=channel) response = channel.capture_input(prompt, speech_recognition_grammar=my_grammar, digit_input_options=my_digit_options) # if response is None, no valid input was detected if response is not None: # check whether we got speech or digits if response.type == response.Type.SPEECH: # return the name return response.input else: # return the name associated with the digit return transfer_list.keys()[int(response.input)] return None def main(channel, application_instance_id, file_man, my_log, application_parameters): my_log.info("Transfer menu started") return_code = 0 try: channel.ring(1) channel.answer() my_log.info("Call answered") # in this application, we will using the high level API wrapper # for the channel object high_level_channel = HighLevelCallChannel(channel, my_log) try: # an extra channel is required, the number of extra channels # available is set on the service page out_channel = channel.ExtraChannel[0] except: raise Error("You need to register an extra channel for this application.") # Convert the application_parameters into a transfer menu list for later use transfer_list = transfer_menu(application_parameters) if not transfer_list: raise Error("Invalid transfer menu") # Prompt for digit channel.FilePlayer.say("Hello. ") target = get_menu_selection(high_level_channel, transfer_list, my_log) if not target: channel.FilePlayer.say("There is nobody to call.") else: # we will use the high level channel to place an outbound call and connect # the inbound and outbound calls together. If the target is a PSTN call # you will have to supply a call_from as well - please see the online documentation # on placing outbound PSTN calls. print("Calling {0}".format(target)) if high_level_channel.call_and_connect(out_channel, call_to=transfer_list[target]) is False: channel.FilePlayer.say("I wasn't able to transfer you. Please try again later.") channel.hang_up() channel.wait_for_idle() except Hangup as exc: my_log.info("Got Hangup") return_code = 100 except Error as exc: my_log.error("Got Error: {0}".format(exc)) return_code = -101 except Exception as exc: my_log.exception("Got unexpected exception: {0}".format(exc)) return_code = -102 finally: if channel.state() != channel.State.IDLE: channel.hang_up() return return_code
-
Filename:
Samples\C#\TransferMenu\TransferMenu.cs
Description:
This application first checks to ensure that the Inbound Service that invoked this application has allocated sufficient extra channels, by inspecting the
channel.ExtraChannels
array. Extra channels are required to make outbound calls in an inbound application and can be configured in the Inbound Service configuration. Once the check has been made, the application splits the applicationParameters into an array for further use.The call is answered, a small TTS message is sent to the user and then the
GetMenuSelection()
method is called. This method will prompt the user for a DTMF tone after giving a list of the transfer options. We use thechannel.DtmfDetector.GetDigits()
method to grab a single keypad press and then make sure the returned item is in our list of transfer options. This loop will continue until either a valid keypress has been detected, or the user hangs up.To transfer the call, we use the
channel.TransferToInboundService()
method. We specify the target address and the extra channel to use for the transfer. The function returns a boolean value indicating if the transfer attempt was successful. This transfer method uses the retrievable transfer mechanism and therefore we must maintain the state of the original channel to allow the transferred call to continue. So we wait for the transferred call to be hung up before terminating.Code:
using System; using System.Threading; using System.Collections.Generic; using AMSClassLibrary; using UASAppAPI; // An inbound application that prompts the user to select a target inbound service // to be transferred to. A prompt (to be read out) and service name (to be transferred to) // for each possible target need to be listed in the applicationParameters when // this service is registered. // // Note: this sample will only run on platforms that support Transfer. // // Requires: // [applicationParameters = semicolon delimited list of <action>,<service name< // e.g. "join a conference,AddToConference; // listen to media file,InboundPlayWav"] // [1 extra channel] namespace TransferMenu { // The application class.join a // 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 TransferMenu : UASInboundApplication { // Possible return codes enum ReturnCode { // Success Codes: Success = 0, // ... any positive integer // Fail Codes: // -1 to -99 reserved ExceptionThrown = -100, NotSufficientExtraChannels = -101, NotSufficientDestinations = -102 } // A class to represent each of the menu options class TransferTarget { public TransferTarget(string entry, int key) { string[] details = entry.Split(','); if (details.Length != 2) { throw new ArgumentException("Invalid destination details", "applicationParameters"); } Prompt = details[0].Trim(); ServiceName = details[1].Trim(); Key = key; } public string Prompt; public string ServiceName; public int Key; } // This is the entry point for the application public override int Run(UASCallChannel channel, string applicationParameters) { this.Trace.TraceInfo("Started"); ReturnCode reply = ReturnCode.Success; bool primaryCallHungUp = false; // Check we've got an extra Channel if (channel.ExtraChannels.Length < 1) { this.Trace.TraceWarning("Needs 1 extra channel to be configured"); return (int)ReturnCode.NotSufficientExtraChannels; } UASCallChannel targetChannel = channel.ExtraChannels[0]; try { // Decipher the applicationParameters string[] entries = applicationParameters.Split(';'); if (entries.Length < 1) { this.Trace.TraceWarning("Needs at least 1 transfer target to be configured"); return (int)ReturnCode.NotSufficientDestinations; } List<TransferTarget> targets = new List<TransferTarget>(entries.Length); int key = 0; foreach (string entry in entries) { targets.Add(new TransferTarget(entry, key++)); } // The selected target TransferTarget target = null; // Answer the call CallState state = channel.Answer(); if (state != CallState.Answered) { primaryCallHungUp = true; } if (!primaryCallHungUp) { this.Trace.TraceInfo("Call answered"); // Prompt for a digit channel.FilePlayer.Say("Hello."); target = getMenuSelection(channel, targets); if (target == null) { primaryCallHungUp = true; } } if (!primaryCallHungUp) { // Transfer to that target channel.FilePlayer.Say("Transferring to {0}", target.Prompt); state = channel.TransferToInboundService( target.ServiceName, targetChannel); if (state != CallState.Transferred) { channel.FilePlayer.Say("I wasn't able to transfer you."); channel.FilePlayer.Say("Please try again later."); } else { // Wait for transferree or far end to hangup channel.WaitForTransferredCallRetrieved(Timeout.Infinite); } } } catch (Exception e) { this.Trace.TraceError("Exception thrown {0}", e.Message); reply = ReturnCode.ExceptionThrown; } finally { channel.HangUp(); } this.Trace.TraceInfo("Completed"); return (int)reply; } TransferTarget getMenuSelection(UASCallChannel channel, List<TransferTarget> targets) { // Prepare to receive digits channel.DtmfDetector.ClearDigits(); // Get the target selected do { // Play the transfer options to the caller with bargein foreach (TransferTarget target in targets) { string msg = string.Format("Press {0} to {1}", target.Key, target.Prompt); FilePlayerCause cause = channel.FilePlayer.Say(msg, true); if (cause == FilePlayerCause.HangUp) { return null; } if (cause == FilePlayerCause.BargeIn) { break; } } string digits; channel.DtmfDetector.GetDigits(1, out digits, 10); if (channel.DtmfDetector.Cause == DtmfDetectorCause.Count) { // Get the transfer target int keyPressed = Convert.ToInt32(digits); this.Trace.TraceInfo("Got keypress {0}", keyPressed); if (keyPressed < targets.Count) { return targets[keyPressed]; } channel.FilePlayer.Say("Selection not valid."); } } while (channel.DtmfDetector.Cause != DtmfDetectorCause.HangUp); // returning null on hangup return null; } } }
-
Filename:
Samples\VB\TransferMenu\TransferMenu.vb
Description:
This application first checks to ensure that the Inbound Service that invoked this application has allocated sufficient extra channels, by inspecting the
channel.ExtraChannels
array. Extra channels are required to make outbound calls in an inbound application and can be configured in the Inbound Service configuration. Once the check has been made, the application splits the applicationParameters into an array for further use.The call is answered, a small TTS message is sent to the user and then the
GetMenuSelection()
method is called. This method will prompt the user for a DTMF tone after giving a list of the transfer options. We use thechannel.DtmfDetector.GetDigits()
method to grab a single keypad press and then make sure the returned item is in our list of transfer options. This loop will continue until either a valid keypress has been detected, or the user hangs up.To transfer the call, we use the
channel.TransferToInboundService()
method. We specify the target address and the extra channel to use for the transfer. The function returns a boolean value indicating if the transfer attempt was successful. This transfer method uses the retrievable transfer mechanism and therefore we must maintain the state of the original channel to allow the transferred call to continue. So we wait for the transferred call to be hung up before terminating.Code:
Imports System.Collections.Generic Imports AMSClassLibrary Imports UASAppAPI ' An inbound application that prompts the user to select a target inbound service ' to be transferred to. A prompt (to be read out) and service name (to be transferred to) ' for each possible target need to be listed in the applicationParameters when ' this service is registered. ' ' Note: this sample will only run on platforms that support Transfer. ' ' Requires: ' [applicationParameters = semicolon delimited list of <action>,<service name> ' e.g. "join a conference,AddToConference;listen to media file,InboundPlayWav"] ' [1 extra channel] Namespace TransferMenu Public Class TransferMenu Inherits UASInboundApplication ' Possible return codes Enum ReturnCode ' Success Codes: Success = 0 ' ... any positive integer ' Fail Codes: ' -1 to -99 reserved ExceptionThrown = -100 NotSufficientExtraChannels = -101 NotSufficientDestinations = -102 End Enum ' A class to represent each of the menu options Class TransferTarget Public Sub New(ByVal entry As String, _ ByVal keyin As Integer) Dim details = entry.Split(",") If details.Length <> 2 Then Throw New ArgumentException("Invalid destination details", "applicationParameters") End If Prompt = details(0).Trim() ServiceName = details(1).Trim() Key = keyin End Sub Public Prompt As String Public ServiceName As String Public Key As Integer End Class ' This is the entry point for the application Overrides Function Run(ByVal channel As UASCallChannel, _ ByVal applicationParameters As String) _ As Integer Me.Trace.TraceInfo("Started") Dim reply = ReturnCode.Success Dim primaryCallHungUp = False ' Check we've got an extra Channel If channel.ExtraChannels.Length < 1 Then Me.Trace.TraceWarning("Needs 1 extra channel to be configured") Return ReturnCode.NotSufficientExtraChannels End If Dim targetChannel = channel.ExtraChannels(0) Try ' Decipher the applicationParameters Dim entries = applicationParameters.Split(";") If entries.Length < 1 Then Me.Trace.TraceWarning("Needs at least 1 transfer target to be configured") Return ReturnCode.NotSufficientDestinations End If Dim targets = New List(Of TransferTarget) Dim key = 0 For Each entry In entries targets.Add(New TransferTarget(entry, key)) key += 1 Next ' The selected target Dim target As TransferTarget = Nothing ' Answer the call Dim state = channel.Answer() If state <> CallState.Answered Then primaryCallHungUp = True End If If Not primaryCallHungUp Then Me.Trace.TraceInfo("Call answered") ' Prompt for a digit channel.FilePlayer.Say("Hello.") target = getMenuSelection(channel, targets) If target Is Nothing Then primaryCallHungUp = True End If End If If Not primaryCallHungUp Then ' Transfer to that target channel.FilePlayer.Say("Transferring to {0}", target.Prompt) state = channel.TransferToInboundService(target.ServiceName, targetChannel) If state <> CallState.Transferred Then channel.FilePlayer.Say("I wasn't able to transfer you.") channel.FilePlayer.Say("Please try again later.") Else ' Wait for transferree or far end to hangup channel.WaitForTransferredCallRetrieved(Timeout.Infinite) End If End If Catch e As Exception Me.Trace.TraceError("Exception thrown {0}", e.Message) reply = ReturnCode.ExceptionThrown Finally channel.HangUp() End Try Me.Trace.TraceInfo("Completed") Return reply End Function Function getMenuSelection(ByVal channel As UASCallChannel, ByVal targets As List(Of TransferTarget)) As TransferTarget ' Prepare to receive digits channel.DtmfDetector.ClearDigits() ' Get the target selected Do ' Play the transfer options to the caller with bargein For Each target In targets Dim msg = String.Format("Press {0} to {1}", target.Key, target.Prompt) Dim cause = channel.FilePlayer.Say(msg, True) If cause = FilePlayerCause.HangUp Then Return Nothing End If If cause = FilePlayerCause.BargeIn Then Exit Do End If Next Dim digits As String = "" channel.DtmfDetector.GetDigits(1, digits, 10) If channel.DtmfDetector.Cause = DtmfDetectorCause.Count Then ' Get the transfer target Dim keyPressed = Convert.ToInt32(digits) Me.Trace.TraceInfo("Got keypress {0}", keyPressed) If keyPressed < targets.Count Then Return targets(keyPressed) End If channel.FilePlayer.Say("Invalid selection.") End If Loop While channel.DtmfDetector.Cause <> DtmfDetectorCause.HangUp ' returning null on hangup Return Nothing End Function End Class End Namespace