Voicemail Lesson 3
We now know how to play and record messages. Let's introduce a few more interesting techniques, including use of the file management system to check for the existence of specific files and use of application parameters to determine the voicemail box that the application is accessing.
-
-
Sample Files:
- Samples\voicemail_3.py
Specifying a Voicemail Box
This step makes use of the
application_parameters
argument that contains the user-defined string originating from the Inbound Service configuration. This string is expected to contain the name we have set up to be our voicemail box. We set up a format for all voicemail filenames that locates them in a voicemailbox subfolder on the cloud.After answering the call the application identifies the level of access for the caller. It does this by comparing the caller Id with the voicemail box name, as determined from
application_parameters
.If the caller Id and voicemail box name match then the caller is deemed to be the owner of the voicemail box and the application calls
top_level_menu()
that allows the caller to listen and delete all messages. In the main function, we can do this using thefile_man.exists()
function, with the filename as a parameter. This will test for the existence of the file on the Cloud. We keep incrementing a counter until the filename is not found.If the caller Id and voicemail box name do not match, the caller is simply allowed to record a new message.
Using the channel
One of the first things we do is to call our
get_caller
function. This extacts information from thecall_from
property of the channel we've been given, which supplies a SIP address. Our telephony system which answers the client encapsulates the phone number information within a SIP format, which is then used by our Cloud. Our function extracts the phone number information from the SIP address.The application parameters are given to us as a string, accessable by the
application_parameters
variable. In our example, we compare the parameter against the caller number, to identify if the caller is actually the owner of our voicemail service. If they are the caller, the application jumps to thetop_level_menu
subroutine, otherwise torecord_new_message
.Dealing with DTMF
The top level menu subroutine uses a function called
get_menu_select
. We saw in the previous example how to access DTMF tones via the keypad. This routine will play some TTS to the user, and then wait for a valid response. Once the user has entered a valid response, the keypad press will be returned. We can then simply check the return code and act appropriately.In
play_all_messages()
, we use a counter, checking if a filename derived from the counter exists, and if so, says some TTS and then the file itself.delete_all_messages()
works in a similar manner, but rather than play each file, we delete the file using thefile_man.delete_file
call. -
__uas_identify__ = "application" __uas_version__ = "1.0b1" from prosody.uas import Hangup, Error, ICanRun """ An inbound application that answers the call and says "Welcome to the voicemail server for <name>". <name> is obtained from the application_parameters and must be registered on the inbound services page of the CWP. The application uses <name> to determine whether the caller owns the voicemail box. It does this by comparing <name> with the <call_from> property of the inbound call. If the caller owns the voicemail box, the top level options given are: 1. Play all existing messages 2. Delete all existing messages If the caller does not own the voicemail box, he can leave a new message. The name of the WAV file to save to will be "samples/voicemail3/<name>/msg<counter>.wav". The <counter> is a variable, initialised to 1, which increments each time a message is successfully saved. This application is part of the online Voice Mail tutorial. """ class VoiceMailBox: def __init__(self, box_name, channel, file_manager, logger): self._channel = channel self._file_manager = file_manager self._logger = logger self._file_format = "samples/voicemail3/" + box_name + "/msg{0}.wav" self._recorded_file_counter = 1 self._set_message_counter() def _set_message_counter(self): # check the message box and set the counter while(self._file_manager.exists(self._file_format.format(self._recorded_file_counter))): self._logger.info("{0} exists".format(self._recorded_file_counter)) self._recorded_file_counter += 1 def say_tts(self, message, bargein=False, beep=False): cause = self._channel.FilePlayer.say(message, barge_in=bargein) if cause == self._channel.FilePlayer.Cause.BARGEIN: return if cause == self._channel.FilePlayer.Cause.NORMAL: if beep is True: cause = self._channel.DTMFPlayer.play("#"); if cause != self._channel.DTMFPlayer.Cause.NORMAL: raise Error("Play tone failed: caused is {0}".format(cause)) else: raise Error("Say '{0}' failed: cause is {1}".format(message, cause)) def play_file(self, filename): cause = self._channel.FilePlayer.play(filename) if cause != self._channel.FilePlayer.Cause.NORMAL: raise Error("Play '{0}' failed: cause is {1}".format(filename, cause)) def record_new_message(self, replace=False): if replace is True and self._recorded_file_counter > 1: self._recorded_file_counter -= 1 filename = self._file_format.format(self._recorded_file_counter) self._recorded_file_counter += 1 self.say_tts("Please record a message after the tone. Press any digit to stop the recording.", beep=True) cause = self._channel.FileRecorder.record(filename, milliseconds_max_silence=3000, seconds_timeout=60, barge_in=True) if cause != self._channel.FileRecorder.Cause.SILENCE and cause != self._channel.FileRecorder.Cause.BARGEIN: raise Error("Record message failed: cause is {0}".format(cause)) # check whether the file has been saved correctly, does it exist? if not self._file_manager.exists(filename): self.say_tts("Your message could not be saved.") else: self.say_tts("The following message has been recorded") self.play_file(filename) def play_all_messages(self): counter = 1 filename = self._file_format.format(counter) while self._file_manager.exists(filename): self.say_tts("Message {0}".format(counter)) self.play_file(filename) counter += 1 filename = self._file_format.format(counter) if counter == 1: self.say_tts("You have no messages to play.") else: self.say_tts("End of messages.") def delete_all_messages(self): counter = 1 filename = self._file_format.format(counter) while self._file_manager.exists(filename): self._file_manager.delete_file(filename) counter += 1 filename = self._file_format.format(counter) if counter == 1: self.say_tts("You have no messages to delete.") else: self.say_tts("All messages deleted.") self._recorded_file_counter = 1 def main(channel, application_instance_id, file_man, my_log, application_parameters): my_log.info ("Voicemail 3 started.") return_code = 0 try: # The voicemail box name must have been configured at service registration if len(application_parameters) == 0: raise Error("Argument list is empty") mailbox = VoiceMailBox(application_parameters, channel, file_man, my_log) channel.ring(2) channel.answer() caller = get_caller(channel) my_log.info ("Call from {0} to {1}'s Voicemail answered".format(caller, application_parameters)) mailbox.say_tts("Welcome to the voicemail server for {0}".format(" ".join(list(application_parameters)))) if caller == application_parameters: top_level_menu(channel, mailbox) else: replace = False # wait on i_can_run to prevent an accidental infinite loop i_can_run = ICanRun(channel) while i_can_run: mailbox.record_new_message(replace) key_selected = get_menu_select(channel, mailbox, "Please press 1 to keep this message or 2 to record an alternative message", 20, "12") if key_selected == '1': mailbox.say_tts("Message saved.") break replace = True mailbox.say_tts("Goodbye.") channel.hang_up(); except Hangup as exc: my_log.info("Completed with Hangup: {0}".format(exc)) return_code = -100 except Error as exc: my_log.error("Completed with Error exception! {0}".format(exc)) return_code = -101 except Exception as exc: my_log.exception("Completed with exception! {0}".format(exc)) return_code = -102 finally: if channel.state() != channel.State.IDLE: channel.hang_up() my_log.info("Voicemail 3 example completed") return return_code def get_caller(channel): caller = channel.Details.call_from # Extract the username from the SIP address if caller.startswith("sip:"): caller = caller[4:] return caller.split('@', 1)[0] def top_level_menu(channel, mailbox): key_selected = get_menu_select(channel, mailbox, "Press 1 to listen to all messages. \ Press 2 to delete all messages. \ Press 3 to quit.", 20, "123") if key_selected == '1': mailbox.play_all_messages() elif key_selected == '2': mailbox.delete_all_messages() def get_menu_select(channel, mailbox, prompt, seconds_repeat_prompt, valid_keys): channel.DTMFDetector.clear_digits() # wait on i_can_run to prevent an accidental infinite loop i_can_run = ICanRun(channel) while i_can_run: mailbox.say_tts(prompt, bargein=True, beep=True) digits = channel.DTMFDetector.get_digits(end=valid_keys, seconds_predigits_timeout=seconds_repeat_prompt) cause = channel.DTMFDetector.cause() if cause == channel.DTMFDetector.Cause.END: return digits[-1] else: mailbox.say_tts("Invalid selection.")
-
-
-
Sample Files:
- Samples\C#\Voicemail3\Voicemail3.cs
Specifying a Voicemail Box
This step makes use of the
applicationArguments
argument that contains the user-defined string originating from the Inbound Service configuration. This string is expected to contain the name we have set up to be our voicemail box. We set up a format for all voicemail filenames that locates them in a voicemailbox subfolder on the cloud.After answering the call the application identifies the level of access for the caller. It does this by comparing the caller Id with the voicemailBox name, as determined from
applicationArguments
. For SIP calls, the voicemailBox name is matched with the Username of the caller (the string before the '@' character) while for PSTN calls the caller Id is simply the calling number.If the caller Id and voicemailBox name match then the caller is deemed to be the owner of the voicemail box and the application calls
TopLevelMenu()
that allows the caller to listen and delete all messages. Otherwise the caller is simply allowed to record a new message.File checking
In the
Run()
method we check for the existence of a file using theFileManager.FileExists()
method. We continually increment therecordedFileCounter
variable until we identify a free filename - this is our next filename for recording.Dealing with DTMF
For menu entry via the keypad, a new method has been created called
GetMenuSelection()
. This reads out a prompt, passed as a parameter, a predigit timeout value, and also any valid selections that the user is able to press. A string of the single digit reply is returned. In theTopLevelMenu()
method, pressing a '1' will call thePlayAllMessages()
method and pressing a '2' will callDeleteAllMessage()
.Voicemail functionality
In
PlayAllMessages()
, a counter is incremented from 1, testing with a derived filename is the file exists, and if so, will play the file. This continues until there are no more messages.DeleteAllMessage()
does a similar job, testing for the existence of a derived filename, but rather than play it, will delete the file with theFileManager.DeleteFile()
method.The
RecordNewMessage()
method behaves like the record method in the previous step, except that it now uses theGetMenuSelection
method to determine user input. -
using System; using System.Threading; using AMSClassLibrary; using UASAppAPI; // An inbound application that answers the call, says "Welcome to the // voicemail server for <name>." // <name> is obtained from the applicationArguments. // It is compared with the <callFrom> property of the inbound call. // // If <callFrom> equals <name> // Say "Press 1 to hear all messages, press 2 to delete all messages". // 1. Play all existing messages // 2. Delete all existing messages // else // Do recording as in voicemail 2. // // All options return to the above menu. // The app only completes when the call is hung up. // The name of the .wav file to save to will be "samples/voicemail3/name/msg<counter>.wav". // The <counter> is a variable, initialised to 1, which increments each time a // message is successfully saved. // // Requires: // [applicationParameters = voicemail box name] namespace Voicemail3 { public class Voicemail3 : UASInboundApplication { // Possible return codes enum ReturnCode { // Success Codes: Success = 0, // ... any positive integer RecordedAMessage = 1, CallHungUp = 2, // Fail Codes: // -1 to -99 reserved ExceptionThrown = -100, ArgumentEmpty = -101, RecordingFailedToStart = -102 } // This is the entry point for the application public override int Run(UASCallChannel channel, string applicationParameters) { this.Trace.TraceInfo("Started"); ReturnCode reply = ReturnCode.Success; // The voicemail box name must have been configured at service registration voicemailBoxName = applicationParameters; if (voicemailBoxName.Length == 0) { return (int)ReturnCode.ArgumentEmpty; } recordFilenameFormat = "samples/voicemail3/" + voicemailBoxName + "/msg{0}.wav"; // Obtain the next free counter recordedFileCounter = 1; while (this.FileManager.FileExists(String.Format(recordFilenameFormat, recordedFileCounter.ToString()))) { recordedFileCounter++; } try { // Answer the call CallState state = channel.Answer(); if (state == CallState.Answered) { string caller = GetCaller(channel); this.Trace.TraceInfo("Call from {0} to {1}'s Voicemail answered", caller, voicemailBoxName); PlayWelcomeMessage(channel, voicemailBoxName); if (caller == voicemailBoxName) { TopLevelMenu(channel); } else { RecordNewMessage(channel); channel.FilePlayer.Say("Goodbye."); } } } catch (Exception e) { this.Trace.TraceError("Exception thrown {0}", e.Message); reply = ReturnCode.ExceptionThrown; } finally { channel.HangUp(); } this.Trace.TraceInfo("Completed"); return (int)reply; } private ReturnCode PlayWelcomeMessage(UASCallChannel channel, string name) { channel.FilePlayer.Say("Welcome to the voicemail server for {0}", name); return ReturnCode.Success; } private string GetCaller(UASCallChannel channel) { // Extract the user part of a SIP address string caller = channel.CallDetails.CallFrom; if (caller.Contains("sip:")) { caller = caller.Substring(4); int pos = caller.IndexOf('@'); if (pos >= 0) { caller = caller.Remove(pos); } } return caller; } private ReturnCode TopLevelMenu(UASCallChannel channel) { ReturnCode reply = ReturnCode.Success; string prompt = "Press 1 to listen to all messages. " + "Press 2 to delete all messages. " + "Press hash to exit."; char keySelected; do { reply = GetMenuSelection(channel, prompt, 20, "12#", out keySelected); if (reply == ReturnCode.Success) { switch (keySelected) { case '1': reply = PlayAllMessages(channel); break; case '2': reply = DeleteAllMessages(channel); break; case '#': return reply; } } } while (reply == ReturnCode.Success); return reply; } private ReturnCode RecordNewMessage(UASCallChannel channel) { ReturnCode reply = ReturnCode.Success; string filename = String.Format(recordFilenameFormat, recordedFileCounter.ToString()); recordedFileCounter++; // Loop round until have recorded a message acceptable to the caller // or caller has hung up bool satisfiedWithMessage = false; do { // Prompt to leave a message channel.FilePlayer.Say("Please record a message after the tone. " + "Press any digit to stop the recording."); // Delay before starting the recording and playing the tone (for effect) Thread.Sleep(1000); // Clear any received digits channel.DtmfDetector.ClearDigits(); // Start recording, enabling barge in this.Trace.TraceInfo("Recording message to file: [{0}]", filename); if (!channel.FileRecorder.Start(filename, true)) { this.Trace.TraceError("Recording failed to start cause = {0}", channel.FileRecorder.Cause); reply = ReturnCode.RecordingFailedToStart; break; } // 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 FileRecorderCause recCause = channel.FileRecorder.WaitUntilFinished(60); if (recCause == FileRecorderCause.HangUp) { this.Trace.TraceInfo("Call has been hung up"); reply = ReturnCode.CallHungUp; break; } if (recCause == FileRecorderCause.Timeout) { this.Trace.TraceInfo("Recording has stopped after 60 seconds"); channel.FileRecorder.Stop(); } // Replay the recorded message channel.FilePlayer.Say("The following message has been recorded"); if (channel.FilePlayer.Play(filename, true) == FilePlayerCause.HangUp) { this.Trace.TraceInfo("Call has been hung up"); reply = ReturnCode.CallHungUp; break; } // Check if the user is satisfied with the recorded message string prompt = "Please press 1 to keep this message or 2 to record an " + "alternative message"; char keySelected; reply = GetMenuSelection(channel, prompt, 20, "12", out keySelected); if (reply == ReturnCode.Success) { if (keySelected == '1') { satisfiedWithMessage = true; channel.FilePlayer.Say("Message saved."); } } } while (!satisfiedWithMessage && (reply == ReturnCode.Success)); return reply; } private ReturnCode PlayAllMessages(UASCallChannel channel) { ReturnCode reply = ReturnCode.Success; int counter = 1; string filename = String.Format(recordFilenameFormat, counter.ToString()); while (this.FileManager.FileExists(filename)) { channel.FilePlayer.Say("Message " + counter.ToString()); channel.FilePlayer.Play(filename); if (channel.FilePlayer.Cause == FilePlayerCause.HangUp) { reply = ReturnCode.CallHungUp; break; } counter++; filename = String.Format(recordFilenameFormat, counter.ToString()); } if (reply == ReturnCode.Success) { channel.FilePlayer.Say("No more messages."); } return reply; } private ReturnCode DeleteAllMessages(UASCallChannel channel) { ReturnCode reply = ReturnCode.Success; int counter = 1; string filename = String.Format(recordFilenameFormat, counter.ToString()); while (this.FileManager.FileExists(filename)) { this.FileManager.DeleteFile(filename); counter++; filename = String.Format(recordFilenameFormat, counter.ToString()); } if (reply == ReturnCode.Success) { channel.FilePlayer.Say("All messages deleted."); } return reply; } // Returns: Success if a valid key was selected, CallHungUp if call hung up. private ReturnCode GetMenuSelection(UASCallChannel channel, string prompt, int secondsRepeatPrompt, string validKeys, out char keySelected) { keySelected = '\0'; // Prepare to receive digits channel.DtmfDetector.ClearDigits(); do { FilePlayerCause cause = channel.FilePlayer.Say(prompt, true); if (cause == FilePlayerCause.HangUp) { return ReturnCode.CallHungUp; } string digits; channel.DtmfDetector.GetDigits(1, out digits, secondsRepeatPrompt); if (channel.DtmfDetector.Cause == DtmfDetectorCause.Count) { if (validKeys.Contains(digits)) { this.Trace.TraceInfo("Got keypress {0}", digits[0]); keySelected = digits[0]; return ReturnCode.Success; } else { channel.FilePlayer.Say("Selection not valid."); } } } while (channel.DtmfDetector.Cause != DtmfDetectorCause.HangUp); // returning on hangup return ReturnCode.CallHungUp; } private string voicemailBoxName; private string recordFilenameFormat; private int recordedFileCounter; } }
-
-
-
Sample Files:
- Samples\VB\Voicemail3\Voicemail3.vb
Specifying a Voicemail Box
This step makes use of the
applicationArguments
argument that contains the user-defined string originating from the Inbound Service configuration. This string is expected to contain the name we have set up to be our voicemail box. We set up a format for all voicemail filenames that locates them in a voicemailbox subfolder on the cloud.After answering the call the application identifies the level of access for the caller. It does this by comparing the caller Id with the voicemailBox name, as determined from
applicationArguments
. For SIP calls, the voicemailBox name is matched with the Username of the caller (the string before the '@' character) while for PSTN calls the caller Id is simply the calling number.If the caller Id and voicemailBox name match then the caller is deemed to be the owner of the voicemail box and the application calls
TopLevelMenu()
that allows the caller to listen and delete all messages. Otherwise the caller is simply allowed to record a new message.File checking
In the
Run()
method we check for the existence of a file using theFileManager.FileExists()
method. We continually increment therecordedFileCounter
variable until we identify a free filename - this is our next filename for recording.Dealing with DTMF
For menu entry via the keypad, a new method has been created called
GetMenuSelection()
. This reads out a prompt, passed as a parameter, a predigit timeout value, and also any valid selections that the user is able to press. A string of the single digit reply is returned. In theTopLevelMenu()
method, pressing a '1' will call thePlayAllMessages()
method and pressing a '2' will callDeleteAllMessage()
.Voicemail functionality
In
PlayAllMessages()
, a counter is incremented from 1, testing with a derived filename is the file exists, and if so, will play the file. This continues until there are no more messages.DeleteAllMessage()
does a similar job, testing for the existence of a derived filename, but rather than play it, will delete the file with theFileManager.DeleteFile()
method.The
RecordNewMessage()
method behaves like the record method in the previous step, except that it now uses theGetMenuSelection
method to determine user input. -
Imports AMSClassLibrary Imports UASAppAPI ' An inbound application that answers the call, says "Welcome to the ' voicemail server for <name>." ' <name> is obtained from the applicationArguments. ' It is compared with the <callFrom> property of the inbound call. ' ' If <callFrom> equals <name> ' Say "Press 1 to hear all messages, press 2 to delete all messages". ' 1. Play all existing messages ' 2. Delete all existing messages ' else ' Do recording as in voicemail 2. ' ' All options return to the above menu. ' The app only completes when the call is hung up. ' The name of the .wav file to save to will be "samples/voicemail3/name/msg<counter>.wav". ' The <counter> is a variable, initialised to 1, which increments each time a ' message is successfully saved. ' ' Requires: ' [applicationParameters = voicemail box name] Namespace Voicemail3 ' 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 Voicemail3 Inherits UASInboundApplication ' Possible return codes Enum ReturnCode ' Success Codes: Success = 0 RecordedAMessage = 1 CallHungUp = 2 ' Fail Codes: ' -1 to -99 reserved ExceptionThrown = -100 ArgumentEmpty = -101 RecordingFailedToStart = -102 End Enum ' 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 As ReturnCode = ReturnCode.Success ' The voicemail box name must have been configured at service registration voicemailBoxName = applicationParameters If voicemailBoxName.Length = 0 Then Return ReturnCode.ArgumentEmpty End If recordFilenameFormat = "samples/voicemail3/" + voicemailBoxName + "/msg{0}.wav" ' Obtain the next free counter recordedFileCounter = 1 While Me.FileManager.FileExists(String.Format(recordFilenameFormat, recordedFileCounter.ToString())) recordedFileCounter += 1 End While Try ' Answer the call Dim state As CallState state = channel.Answer() If state = CallState.Answered Then Dim caller = GetCaller(channel) Me.Trace.TraceInfo("Call from {0} to {1}'s Voicemail answered", _ caller, voicemailBoxName) PlayWelcomeMessage(channel, voicemailBoxName) If caller = voicemailBoxName Then TopLevelMenu(channel) Else RecordNewMessage(channel) channel.FilePlayer.Say("Goodbye.") End If 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 Private Function PlayWelcomeMessage(ByVal channel As UASCallChannel, ByVal name As String) As ReturnCode channel.FilePlayer.Say("Welcome to the voicemail server for {0}", name) Return ReturnCode.Success End Function Private Function GetCaller(ByVal channel As UASCallChannel) As String ' Extract the user part of a SIP address Dim caller = channel.CallDetails.CallFrom If caller.Contains("sip:") Then caller = caller.Substring(4) Dim pos = caller.IndexOf("@") If (pos >= 0) Then caller = caller.Remove(pos) End If End If Return caller End Function Private Function TopLevelMenu(ByVal channel As UASCallChannel) As ReturnCode Dim reply = ReturnCode.Success Dim prompt = "Press 1 to listen to all messages. " + _ "Press 2 to delete all messages. " + _ "Press hash to exit." Dim keySelected As Char Do reply = GetMenuSelection(channel, prompt, 20, "12#", keySelected) If reply = ReturnCode.Success Then Select Case keySelected Case "1" reply = PlayAllMessages(channel) Case "2" reply = DeleteAllMessages(channel) Case "#" Return reply End Select End If Loop While reply = ReturnCode.Success Return reply End Function Private Function RecordNewMessage(ByVal channel As UASCallChannel) As ReturnCode Dim reply = ReturnCode.Success Dim filename = String.Format(recordFilenameFormat, recordedFileCounter.ToString()) recordedFileCounter += 1 ' Loop round until have recorded a message acceptable to the caller ' or caller has hung up Dim satisfiedWithMessage = False Do ' Prompt to leave a message channel.FilePlayer.Say("Please record a message after the tone. " + _ "Press any digit to stop the recording.") ' Delay before starting the recording and playing the tone (for effect) Thread.Sleep(1000) ' Clear any received digits channel.DtmfDetector.ClearDigits() ' Start recording, enabling barge in Me.Trace.TraceInfo("Recording message to file: [{0}]", filename) If Not channel.FileRecorder.Start(filename, True) Then Me.Trace.TraceError("Recording failed to start cause = {0}", _ channel.FileRecorder.Cause) reply = ReturnCode.RecordingFailedToStart Exit Do End If ' 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(60) If recCause = FileRecorderCause.HangUp Then Me.Trace.TraceInfo("Call has been hung up") reply = ReturnCode.CallHungUp Exit Do End If If recCause = FileRecorderCause.Timeout Then Me.Trace.TraceInfo("Recording has stopped after 60 seconds") channel.FileRecorder.Stop() End If ' Replay the recorded message channel.FilePlayer.Say("The following message has been recorded") If channel.FilePlayer.Play(filename, True) = FilePlayerCause.HangUp Then Me.Trace.TraceInfo("Call has been hung up") reply = ReturnCode.CallHungUp Exit Do End If ' Check if the user is satisfied with the recorded message Dim prompt = "Please press 1 to keep this message or 2 to record an " + _ "alternative message" Dim keySelected As Char reply = GetMenuSelection(channel, prompt, 20, "12", keySelected) If reply = ReturnCode.Success Then If keySelected = "1" Then satisfiedWithMessage = True channel.FilePlayer.Say("Message saved.") End If End If Loop While Not satisfiedWithMessage And (reply = ReturnCode.Success) Return reply End Function Private Function PlayAllMessages(ByVal channel As UASCallChannel) As ReturnCode Dim reply = ReturnCode.Success Dim counter = 1 Dim filename = String.Format(recordFilenameFormat, counter.ToString()) While Me.FileManager.FileExists(filename) channel.FilePlayer.Say("Message " + counter.ToString()) channel.FilePlayer.Play(filename) If channel.FilePlayer.Cause = FilePlayerCause.HangUp Then reply = ReturnCode.CallHungUp Exit While End If counter += 1 filename = String.Format(recordFilenameFormat, counter.ToString()) End While If reply = ReturnCode.Success Then channel.FilePlayer.Say("No more messages.") End If Return reply End Function Private Function DeleteAllMessages(ByVal channel As UASCallChannel) As ReturnCode Dim reply = ReturnCode.Success Dim counter = 1 Dim filename = String.Format(recordFilenameFormat, counter.ToString()) While Me.FileManager.FileExists(filename) Me.FileManager.DeleteFile(filename) counter += 1 filename = String.Format(recordFilenameFormat, counter.ToString()) End While If reply = ReturnCode.Success Then channel.FilePlayer.Say("All messages deleted.") End If Return reply End Function ' Returns: Success if a valid key was selected, CallHungUp if call hung up. Private Function GetMenuSelection(ByVal channel As UASCallChannel, ByVal prompt As String, ByVal secondsRepeatPrompt As Integer, ByVal validKeys As String, ByRef keySelected As Char) As ReturnCode keySelected = "\0" ' Prepare to receive digits channel.DtmfDetector.ClearDigits() Do Dim cause = channel.FilePlayer.Say(prompt, True) If cause = FilePlayerCause.HangUp Then Return ReturnCode.CallHungUp End If Dim digits As String = "" channel.DtmfDetector.GetDigits(1, digits, secondsRepeatPrompt) If channel.DtmfDetector.Cause = DtmfDetectorCause.Count Then If (validKeys.Contains(digits)) Then Me.Trace.TraceInfo("Got keypress {0}", digits(0)) keySelected = digits(0) Return ReturnCode.Success Else channel.FilePlayer.Say("Invalid selection.") End If End If Loop While channel.DtmfDetector.Cause <> DtmfDetectorCause.HangUp ' returning on hangup Return ReturnCode.CallHungUp End Function Private voicemailBoxName As String Private recordFilenameFormat As String Private recordedFileCounter As String End Class End Namespace
-
Next time...
So, we have a simple bare-bone voicemail system up and running. We could personalise the voicemail box some more though. How about allowing the owner of the voicemail box to record their own introduction message and use the file as a welcome message? Additionally we could allow the owner to select messages to keep or delete. Read on to find out how...
Lesson 4