""" 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("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 # 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