""" 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 (these are configured on the inbound service page of the CWP). For information on placing outbound calls please read the online documentation at: https://cloud.aculab.com/documents/web_services_outbound 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 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("Hello, please record a message after the beep.") 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 while True: # 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