public async Task can_send_command() { using (var listener = new FakeFreeSwitchListener(0)) { listener.Start(); listener.Connections.Subscribe( async socket => { socket.MessagesReceived.Where(m => m.Equals("auth ClueCon")) .Take(1) .Subscribe(async m => { await socket.SendCommandReplyOk(); }); socket.MessagesReceived.Where(m => m.StartsWith("test")) .Take(1) .Subscribe( async m => { await socket.SendCommandReplyOk(); }); await socket.Send("Content-Type: auth/request"); }); using (var client = await InboundSocket.Connect("127.0.0.1", listener.Port, "ClueCon")) { var result = await client.SendCommand("test"); Assert.True(result.Success); } } }
public async Task when_a_command_reply_error_is_received_in_response_to_an_application_request_it_should_return_a_failed_ApplicationResult() { using (var listener = new FakeFreeSwitchListener(0)) { listener.Start(); listener.Connections.Subscribe( async socket => { socket.MessagesReceived.Where(m => m.Equals("auth ClueCon")) .Take(1) .Subscribe(async m => { await socket.SendCommandReplyOk(); }); socket.MessagesReceived.Where(m => m.StartsWith("sendmsg")) .Take(1) .Subscribe( async m => { await socket.SendCommandReplyError("invalid session id [c1cdaeae-ebb0-4f3f-8f75-0f673bfbc046]"); }); await socket.Send("Content-Type: auth/request"); }); using (var client = await InboundSocket.Connect("127.0.0.1", listener.Port, "ClueCon")) { var result = await client.Play("c1cdaeae-ebb0-4f3f-8f75-0f673bfbc046", "test.wav"); Assert.False(result.Success); } } }
public void an_invalid_password_should_throw_an_InboundSocketConnectionFailedException() { using (var listener = new FakeFreeSwitchListener(0)) { listener.Start(); bool authRequestReceived = false; listener.Connections.Subscribe( async socket => { socket.MessagesReceived.Where(m => m.StartsWith("auth")) .Subscribe(async m => { authRequestReceived = true; await socket.SendCommandReplyError("Invalid Password"); }); await socket.Send("Content-Type: auth/request"); }); var aggregateException = Record.Exception(() => InboundSocket.Connect("127.0.0.1", listener.Port, "WrongPassword").Wait()); Assert.True(authRequestReceived); Assert.IsType <InboundSocketConnectionFailedException>(aggregateException.InnerException); Assert.Equal("Invalid password when trying to connect to 127.0.0.1:" + listener.Port, aggregateException.InnerException.Message); } }
private static async void CallTracking() { var client = await InboundSocket.Connect("10.10.10.36", 8021, "ClueCon"); string uuid = null; client.Events.Where(x => x.EventName == EventName.ChannelAnswer) .Subscribe(x => { uuid = x.UUID; ColorConsole.WriteLine("Channel Answer Event ".Blue(), x.UUID); }); client.Events.Where(x => x.EventName == EventName.ChannelHangup) .Subscribe(x => { uuid = null; ColorConsole.WriteLine("Channel Hangup Event ".Blue(), x.UUID); }); ColorConsole.WriteLine("Press enter to hang up the current call".Green()); Console.ReadLine(); if (uuid != null) { ColorConsole.WriteLine("Hanging up ".Green(), uuid); await client.Play(uuid, "ivr/8000/ivr-call_rejected.wav"); await client.Hangup(uuid, HangupCause.CallRejected); } client.Exit(); }
private static async void ApiTest() { using (var client = await InboundSocket.Connect("localhost", 8021, "ClueCon")) { ColorConsole.WriteLine((await client.SendApi("status")).BodyText.DarkBlue()); ColorConsole.WriteLine((await client.SendApi("blah")).BodyText.DarkBlue()); ColorConsole.WriteLine((await client.SendApi("status")).BodyText.DarkBlue()); } }
public async Task Run(CancellationToken cancellationToken) { using (var client = await InboundSocket.Connect("localhost", 8021, "ClueCon")) { ColorConsole.WriteLine((await client.SendApi("status")).BodyText.DarkBlue()); ColorConsole.WriteLine((await client.SendApi("invalid_api_command")).BodyText.DarkRed()); ColorConsole.WriteLine((await client.SendApi("sofia status")).BodyText.DarkBlue()); ColorConsole.WriteLine((await client.SendApi("show bridged_calls")).BodyText.DarkBlue()); } }
public void when_no_AuthRequest_received_it_should_throw_TimeoutException() { using (var listener = new FakeFreeSwitchListener(0)) { listener.Start(); var aggregateException = Record.Exception(() => InboundSocket.Connect("127.0.0.1", listener.Port, "ClueCon", TimeSpan.FromMilliseconds(100)).Wait()); Assert.IsType <TimeoutException>(aggregateException.InnerException); } }
public async Task when_a_CHANNEL_EXECUTE_COMPLETE_event_is_returned_it_should_complete_the_Application() { using (var listener = new FakeFreeSwitchListener(0)) { listener.Start(); listener.Connections.Subscribe( async socket => { socket.MessagesReceived.Where(m => m.Equals("auth ClueCon")) .Take(1) .Subscribe(async m => { await socket.SendCommandReplyOk(); }); socket.MessagesReceived.Where(m => m.StartsWith("event")) .Take(1) .Subscribe(async m => await socket.SendCommandReplyOk()); socket.MessagesReceived.Where(m => m.StartsWith("sendmsg")) .Take(1) .Subscribe( async m => { var regex = new Regex(@"sendmsg (?<channelUUID>\S+)\nEvent-UUID: (?<applicationUUID>\S+)\n"); var matches = regex.Match(m); var channelUUID = matches.Groups["channelUUID"].Value; var applicationUUID = matches.Groups["applicationUUID"].Value; var channelExecuteComplete = TestMessages.PlaybackComplete .Replace("Application-UUID: fd3ababd-ad60-4582-8c6c-609064d55fe7", "Application-UUID: " + applicationUUID) .Replace("Unique-ID: 4e1cfa50-4c2f-44c9-aaf3-8ca590bed0e4", "Unique-ID: " + channelUUID) .Replace("\r\n", "\n"); await socket.SendCommandReplyOk(); await socket.Send(channelExecuteComplete); }); await socket.Send("Content-Type: auth/request"); }); using (var client = await InboundSocket.Connect("127.0.0.1", listener.Port, "ClueCon")) { var result = await client.Play("4e1cfa50-4c2f-44c9-aaf3-8ca590bed0e4", "test.wav"); Assert.True(result.Success); Assert.Equal("FILE PLAYED", result.ResponseText); } } }
public async Task when_FreeSwitch_disconnects_it_should_complete_the_observables() { using (var listener = new FakeFreeSwitchListener(0)) { listener.Start(); bool disconnected = false; listener.Connections.Subscribe( async socket => { socket.MessagesReceived.Where(m => m.Equals("auth ClueCon")) .Take(1) .Subscribe(async m => { await socket.SendCommandReplyOk(); }); socket.MessagesReceived.Where(m => m.StartsWith("exit")) .Take(1) .Subscribe( async m => { await socket.SendCommandReplyOk(); await socket.SendDisconnectNotice(); socket.Dispose(); disconnected = true; }); await socket.Send("Content-Type: auth/request"); }); using (var client = await InboundSocket.Connect("127.0.0.1", listener.Port, "ClueCon")) { bool completed = false; client.Messages.Subscribe(_ => { }, ex => { }, () => completed = true); await client.Exit(); await Wait.Until(() => disconnected); Console.WriteLine("Disconnected, completed:" + completed); await Wait.Until(() => completed); Assert.True(completed); } } }
public async Task when_no_api_response_received_it_should_throw_a_TimeOutException() { using (var listener = new FakeFreeSwitchListener(0)) { listener.Start(); bool apiRequestReceived = false; listener.Connections.Subscribe( async socket => { socket.MessagesReceived.FirstAsync(m => m.Equals("auth ClueCon")) .Subscribe(async m => { await socket.SendCommandReplyOk(); }); socket.MessagesReceived.FirstAsync(m => m.StartsWith("api")) .Subscribe(async m => { apiRequestReceived = true; await Task.Delay(1000); await socket.SendApiResponseError("error"); }); socket.MessagesReceived.Where(m => m.Equals("exit")) .Subscribe( async _ => { await socket.SendCommandReplyOk(); await socket.SendDisconnectNotice(); }); await socket.Send("Content-Type: auth/request"); }); using (var client = InboundSocket.Connect("127.0.0.1", listener.Port, "ClueCon", TimeSpan.FromMilliseconds(100)).Result) { client.ResponseTimeOut = TimeSpan.FromMilliseconds(100); var ex = Record.Exception(() => client.SendApi("status").Wait()); Assert.NotNull(ex); Assert.IsType <TimeoutException>(ex.InnerException); Assert.True(apiRequestReceived); await client.Exit(); } } }
private static async void DtmfTest() { var client = await InboundSocket.Connect("10.10.10.36", 8021, "ClueCon"); var originate = await client.Originate( "user/1005", new OriginateOptions { CallerIdNumber = "123456789", CallerIdName = "Dan Leg A", HangupAfterBridge = false, TimeoutSeconds = 20 }); if (!originate.Success) { ColorConsole.WriteLine("Originate Failed ".Red(), originate.HangupCause.ToString()); client.Exit(); } else { var uuid = originate.ChannelData.UUID; await client.SubscribeEvents(EventName.Dtmf); await client.SetMultipleChannelVariables(uuid, "dtmf_verbose=true", "drop_dtmf=true"); //"min_dup_digit_spacing_ms=500", //"spandsp_dtmf_rx_threshold=-32"); //"spandsp_dtmf_rx_twist=32", //"spandsp_dtmf_rx_reverse_twist=7"); await client.ExecuteApplication(uuid, "spandsp_start_dtmf"); client.OnHangup(uuid, e => { ColorConsole.WriteLine("Hangup Detected on A-Leg {0} {1}".Red(), e.Headers[HeaderNames.CallerUniqueId], e.Headers[HeaderNames.HangupCause]); client.Exit(); }); client.Events.Where(x => x.UUID == uuid && x.EventName == EventName.Dtmf) .Subscribe(e => Console.WriteLine(e.Headers[HeaderNames.DtmfDigit])); } }
public void when_no_response_to_auth_received_it_should_throw_TimeoutException() { using (var listener = new FakeFreeSwitchListener(0)) { listener.Start(); listener.Connections.Subscribe( async socket => { await socket.Send("Content-Type: auth/request"); }); var aggregateException = Record.Exception(() => InboundSocket.Connect("127.0.0.1", listener.Port, "ClueCon", TimeSpan.FromMilliseconds(100)).Wait()); Assert.IsType <TimeoutException>(aggregateException.InnerException); } }
public async Task sending_a_correct_password_should_connect() { using (var listener = new FakeFreeSwitchListener(0)) { listener.Start(); bool authRequestReceived = false; bool exitRequestReceived = false; listener.Connections.Subscribe( async socket => { socket.MessagesReceived.Where(m => m.Equals("auth ClueCon")) .Subscribe(async m => { authRequestReceived = true; await socket.SendCommandReplyOk(); }); socket.MessagesReceived.Where(m => m.Equals("exit")) .Subscribe( async _ => { exitRequestReceived = true; await socket.SendCommandReplyOk(); await socket.SendDisconnectNotice(); }); await socket.Send("Content-Type: auth/request"); }); using (var client = await InboundSocket.Connect("127.0.0.1", listener.Port, "ClueCon")) { Assert.True(authRequestReceived); await client.Exit(); await Wait.Until(() => exitRequestReceived); Assert.True(exitRequestReceived); } } }
public static async void Run() { var client = await InboundSocket.Connect("192.168.99.66", 8021, "12345"); var res = await client.Api("status"); Console.WriteLine(res.BodyText); await client.SubscribeEvents(new EventName[] { EventName.PlaybackStop }); client.Events.Subscribe(evnt => { Console.WriteLine(evnt.EventName); foreach (var v in evnt.Headers) { Console.WriteLine(v.Key + " " + v.Value); } Console.WriteLine(); Console.WriteLine(); }); }
public async Task Run(CancellationToken cancellationToken) { client = await InboundSocket.Connect("127.0.0.1", 8021, "ClueCon", TimeSpan.FromSeconds(20)); var originate = await client.Originate( "user/1000", new OriginateOptions { CallerIdNumber = "123456789", CallerIdName = "Dan Leg A", HangupAfterBridge = false, TimeoutSeconds = 20 }); if (!originate.Success) { ColorConsole.WriteLine("Originate Failed ".Red(), originate.HangupCause.ToString()); await client.Exit(); } else { var uuid = originate.ChannelData.UUID; await client.SubscribeEvents(EventName.Dtmf); //uncomment to play with mod_spandsp inband dtmf detection //note: i could not get this to work on windows, works fine on linux //await client.SetMultipleChannelVariables(uuid, //"min_dup_digit_spacing_ms=500", //"spandsp_dtmf_rx_threshold=-32"); //"spandsp_dtmf_rx_twist=32", //"spandsp_dtmf_rx_reverse_twist=7"); //await client.ExecuteApplication(uuid, "spandsp_start_dtmf"); client.OnHangup( uuid, e => { ColorConsole.WriteLine( "Hangup Detected on A-Leg".Red(), e.Headers[HeaderNames.CallerUniqueId], e.Headers[HeaderNames.HangupCause]); client.Exit(); }); client.ChannelEvents.Where(x => x.UUID == uuid && x.EventName == EventName.Dtmf).Subscribe( e => { Console.WriteLine("Got DTMF"); Console.WriteLine(e.UUID == uuid); Console.WriteLine("UIIDS: event {0} ours {1}", e.UUID, uuid); Console.WriteLine(e.Headers[HeaderNames.DtmfDigit]); }); ColorConsole.WriteLine("Press [Enter] to exit.".Green()); await Util.WaitForEnterKeyPress(cancellationToken); } }
public Task Run(CancellationToken cancellationToken) { int authFailures = 0; int heartbeatsReceived = 0; var settings = commandLineReader.ReadObject <LoadTestSettings>(cancellationToken); ColorConsole.WriteLine("Spinning up ".DarkGreen(), settings.MaxClients.ToString().Green(), " InboundSockets".DarkGreen()); ColorConsole.WriteLine("They will connect and subscribe to HeartBeat events then disconnect when the first Heartbeat has been received.".DarkGreen()); Parallel.For(0, settings.MaxClients, async(_) => { long clientId = 0; if (cancellationToken.IsCancellationRequested) { return; } try { using ( InboundSocket client = await InboundSocket.Connect( "127.0.0.1", 8021, "ClueCon", TimeSpan.FromSeconds(settings.ConnectionTimeoutSeconds))) { clientId = client.Id; await client.SubscribeEvents(EventName.Heartbeat); EventMessage heartbeat = await client.Events.FirstOrDefaultAsync(x => x.EventName == EventName.Heartbeat).ToTask(cancellationToken); if (heartbeat != null) { Interlocked.Increment(ref heartbeatsReceived); ColorConsole.WriteLine("Client ".DarkCyan(), clientId.ToString(), " reporting in ".DarkCyan()); } } } catch (InboundSocketConnectionFailedException ex) { if (ex.InnerException != null && ex.InnerException is TimeoutException) { ColorConsole.WriteLine("Auth Timeout".OnDarkRed()); } else { ColorConsole.WriteLine("Connection failure ".OnDarkRed(), ex.Message.DarkRed()); } Interlocked.Increment(ref authFailures); } catch (TaskCanceledException) { } }); ColorConsole.WriteLine("Press [Enter] to exit.".Green()); Console.ReadLine(); ColorConsole.WriteLine("THere were {0} heartbeats".Fmt(heartbeatsReceived).Green()); ColorConsole.WriteLine("THere were {0} auth timeout failures".Fmt(authFailures).Red()); return(Task.FromResult(0)); }
public async Task Run(CancellationToken cancellationToken) { //cancellationToken is cancelled when Ctrl+C is pressed //we'll use our own inner cancellationToken in our business logic //and link it to the outer one that is provided. var ourCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); using (var client = await InboundSocket.Connect()) { Console.WriteLine("Authenticated!"); await client.SubscribeEvents(EventName.ChannelHangup, EventName.BackgroundJob); client.ChannelEvents.Where(x => x.EventName == EventName.ChannelHangup && x.HangupCause != HangupCause.NormalClearing) .Subscribe(x => { Console.WriteLine("Hangup Detected : {0} {1}", x.GetVariable("mobile_no"), x.HangupCause); }); using (var listener = new OutboundListener(8084)) { listener.Connections.Subscribe( async socket => { try { await socket.Connect(); await socket.Filter(HeaderNames.UniqueId, socket.ChannelData.UUID); var uuid = socket.ChannelData.Headers[HeaderNames.UniqueId]; Console.WriteLine( "OutboundSocket connected for channel {0} {1}", uuid, socket.ChannelData.GetVariable("mobile_no")); await socket.Play(uuid, "misc/8000/misc-learn_more_about_freeswitch_solutions.wav"); await socket.Play(uuid, "misc/8000/misc-freeswitch_is_state_of_the_art.wav"); await socket.ExecuteApplication(uuid, "sleep", "1000"); //wait for audio to go out to the network await socket.Hangup(uuid, HangupCause.NormalClearing); } catch (OperationCanceledException) { //hangup - freeswitch disconnected from us } }); listener.Start(); var checkCallCount = new Task( async() => { try { while (!ourCancellationToken.IsCancellationRequested) { var res = await client.SendApi("show calls count"); Console.WriteLine("Current Calls Count " + Convert.ToInt32(res.BodyText.Split(' ')[0])); currentCallCount = Convert.ToInt32(res.BodyText.Split(' ')[0]); await Task.Delay(2000); } } catch (OperationCanceledException) { //shutdown } }); checkCallCount.Start(); Task.Run( async() => { try { await Dialler(client, ourCancellationToken.Token); } catch (OperationCanceledException) { //shutdown } }); ColorConsole.WriteLine("Press [Enter] to exit.".Green()); await Util.WaitForEnterKeyPress(cancellationToken); ourCancellationToken.Cancel(); listener.Dispose(); } } }
public async Task Run(CancellationToken cancellationToken) { client = await InboundSocket.Connect(); Console.WriteLine("Authenticated!"); try { await client.SubscribeEvents(EventName.Dtmf); var originate = await client.Originate( "user/1000", new OriginateOptions { CallerIdNumber = "123456789", CallerIdName = "Dan Leg A", HangupAfterBridge = false, TimeoutSeconds = 20, }); if (!originate.Success) { ColorConsole.WriteLine("Originate Failed ".Red(), originate.HangupCause.ToString()); await client.Exit(); } else { var uuid = originate.ChannelData.Headers[HeaderNames.CallerUniqueId]; ColorConsole.WriteLine("Originate success ".Green(), originate.ChannelData.Headers[HeaderNames.AnswerState]); var recordingPath = "{0}.wav".Fmt(uuid); //"c:/temp/recording_{0}.wav".Fmt(uuid); //"$${recordings_dir}/" + "{0}.wav".Fmt(uuid); //"c:/temp/recording_{0}.wav".Fmt(uuid); client.OnHangup( uuid, e => { ColorConsole.WriteLine( "Hangup Detected on A-Leg ".Red(), e.Headers[HeaderNames.CallerUniqueId], " ", e.Headers[HeaderNames.HangupCause]); client.Exit(); }); await client.Play(uuid, "ivr/ivr-call_being_transferred.wav"); var bridgeUUID = Guid.NewGuid().ToString(); var ringingHandler = client.ChannelEvents.Where(x => x.UUID == bridgeUUID && x.EventName == EventName.ChannelProgress) .Take(1) .Subscribe(e => ColorConsole.WriteLine("Progress {0} on {1}".Fmt(e.AnswerState, e.UUID).Blue())); var bridge = await client.Bridge( uuid, "user/1003", new BridgeOptions() { UUID = bridgeUUID, TimeoutSeconds = 20, //CallerIdName = "Dan B Leg", //CallerIdNumber = "987654321", //HangupAfterBridge = false, //IgnoreEarlyMedia = true, //ContinueOnFail = true, //RingBack = "tone_stream://${uk-ring};loops=-1", //ConfirmPrompt = "ivr/8000/ivr-to_accept_press_one.wav", //ConfirmInvalidPrompt = "ivr/8000/ivr-that_was_an_invalid_entry.wav", //ConfirmKey = "1234", }); if (!bridge.Success) { ringingHandler.Dispose(); ColorConsole.WriteLine("Bridge failed ".Red(), bridge.ResponseText); await client.Play(uuid, "ivr/ivr-call_rejected.wav"); await client.Hangup(uuid, HangupCause.CallRejected); } else { ColorConsole.WriteLine( "Bridge succeeded from {0} to {1} - {2}".Fmt(bridge.ChannelData.UUID, bridge.BridgeUUID, bridge.ResponseText) .Green()); //when b-leg hangs up, play a notification to a-leg client.OnHangup( bridge.BridgeUUID, async e => { ColorConsole.WriteLine( "Hangup Detected on B-Leg ".Red(), e.Headers[HeaderNames.CallerUniqueId], " ", e.Headers[HeaderNames.HangupCause]); await client.Play(uuid, "ivr/ivr-you_may_exit_by_hanging_up.wav"); await client.Hangup(uuid, HangupCause.NormalClearing); }); await client.SetChannelVariable(uuid, "RECORD_ARTIST", "'Opex Hosting Ltd'"); await client.SetChannelVariable(uuid, "RECORD_MIN_SEC", 0); await client.SetChannelVariable(uuid, "RECORD_STEREO", "true"); var recordingResult = await client.SendApi("uuid_record {0} start {1}".Fmt(uuid, recordingPath)); ColorConsole.WriteLine(("Recording... " + recordingResult.Success).Green()); if (recordingResult.Success) { client.ChannelEvents.Where(x => x.UUID == uuid && x.EventName == EventName.Dtmf).Subscribe( async(e) => { var dtmf = e.Headers[HeaderNames.DtmfDigit]; switch (dtmf) { case "1": ColorConsole.WriteLine("Mask recording".Green()); await client.SendApi("uuid_record {0} mask {1}".Fmt(uuid, recordingPath)); await client.ExecuteApplication( uuid, "displace_session", applicationArguments: "{0} m".Fmt("ivr/ivr-recording_paused.wav")); break; case "2": ColorConsole.WriteLine("Unmask recording".Green()); await client.SendApi("uuid_record {0} unmask {1}".Fmt(uuid, recordingPath)); await client.ExecuteApplication( uuid, "displace_session", applicationArguments: "{0} m".Fmt("ivr/ivr-begin_recording.wav")); break; case "3": ColorConsole.WriteLine("Stop recording".Green()); await client.SendApi("uuid_record {0} stop {1}".Fmt(uuid, recordingPath)); await client.ExecuteApplication( uuid, "displace_session", applicationArguments: "{0} m".Fmt("ivr/ivr-recording_stopped.wav")); break; } }); } } } } catch (TaskCanceledException) { ColorConsole.WriteLine("TaskCancelled - shutting down".OnRed()); client.Dispose(); } ColorConsole.WriteLine("Press [Enter] to exit.".Green()); await Util.WaitForEnterKeyPress(cancellationToken); }
public async Task Run(CancellationToken cancellationToken) { client = await InboundSocket.Connect("127.0.0.1", 8021, "ClueCon", TimeSpan.FromSeconds(20)); await client.SubscribeEvents(EventName.Dtmf, EventName.ChannelHangup); var originate = await client.Originate( "user/1000", new OriginateOptions { CallerIdNumber = "123456789", CallerIdName = "Dan Leg A", HangupAfterBridge = false, TimeoutSeconds = 20 }); if (!originate.Success) { ColorConsole.WriteLine("Originate Failed ".Blue(), originate.HangupCause.ToString()); await client.Exit(); } else { ColorConsole.WriteLine("{0} {1} {2}".Fmt(originate.ChannelData.EventName, originate.ChannelData.AnswerState, originate.ChannelData.ChannelState).Blue()); var uuid = originate.ChannelData.UUID; await client.SetChannelVariable(uuid, "dtmf_verbose", "true"); //await client.StartDtmf(uuid); client.Events.Where(x => x.UUID == uuid && x.EventName == EventName.ChannelHangup) .Subscribe( e => { ColorConsole.WriteLine("Hangup Detected on A-Leg ".Red(), e.UUID, e.HangupCause.ToString()); client.Exit(); }); client.Events.Where(x => x.UUID == uuid && x.EventName == EventName.Dtmf) .Subscribe(e => ColorConsole.WriteLine("DTMF Detected ".Blue(), e.Headers[HeaderNames.DtmfDigit])); var playGetDigitsResult = await client.PlayGetDigits( uuid, new PlayGetDigitsOptions() { MinDigits = 4, MaxDigits = 8, MaxTries = 3, TimeoutMs = 4000, TerminatorDigits = "#", PromptAudioFile = "ivr/ivr-please_enter_pin_followed_by_pound.wav", BadInputAudioFile = "ivr/ivr-that_was_an_invalid_entry.wav", DigitTimeoutMs = 2000, }); ColorConsole.WriteLine("Got digits: ".Blue(), playGetDigitsResult.Digits); if (playGetDigitsResult.Success) { await client.Play(uuid, "ivr/ivr-you_entered.wav"); await client.Say( uuid, new SayOptions() { Text = playGetDigitsResult.Digits, Type = SayType.Number, Method = SayMethod.Iterated }); await client.Play( uuid, "ivr/ivr-you_may_exit_by_hanging_up.wav", new PlayOptions() { Loops = 3 }); await client.Hangup(uuid, HangupCause.CallRejected); } } }