/// <summary> /// Register a callback to be invoked when the given Channel UUID hangs up. /// </summary> /// <param name="uuid">The Channel UUID.</param> /// <param name="action">A Callback to be invoked on hangup.</param> public void OnHangup(string uuid, Action <EventMessage> action) { ChannelEvents.Where(x => x.UUID == uuid && x.EventName == EventName.ChannelHangup).Take(1).Subscribe(action); }
/// <summary> /// Asynchronously executes a dialplan application on the given channel. /// </summary> /// <remarks> /// See https://freeswitch.org/confluence/display/FREESWITCH/mod_dptools /// </remarks> /// <param name="uuid">The channel UUID.</param> /// <param name="application">The dialplan application to execute.</param> /// <param name="applicationArguments">(Optional) arguments to pass to the application.</param> /// <param name="eventLock">(Default: false) Whether to block the socket until the application completes before processing further. /// (see https://wiki.freeswitch.org/wiki/Event_Socket_Outbound#Q:_Ordering_and_async_keyword )</param> /// <param name="async">(Default: false) Whether to return control from the application immediately. /// (see https://wiki.freeswitch.org/wiki/Event_Socket_Outbound#Q:_Should_I_use_sync_mode_or_async_mode.3F) /// </param> /// <param name="loops">(Optional) How many times to repeat the application.</param> /// <returns> /// A Task of <seealso cref="EventMessage"/> that wraps the ChannelExecuteComplete event if the application completes successfully. /// The Task result will be null if the application did not execute, for example, the socket disconnected or the channel was hung up. /// </returns> public Task <ChannelEvent> ExecuteApplication( string uuid, string application, string applicationArguments = null, bool eventLock = false, bool async = false, int loops = 1) { if (uuid == null) { throw new ArgumentNullException("uuid"); } if (application == null) { throw new ArgumentNullException("application"); } //lists.freeswitch.org/pipermail/freeswitch-users/2013-May/095329.html var applicationUUID = Guid.NewGuid().ToString(); var sb = StringBuilderPool.Allocate(); sb.AppendFormat("sendmsg {0}\nEvent-UUID: {1}\ncall-command: execute\nexecute-app-name: {2}\n", uuid, applicationUUID, application); if (eventLock) { sb.Append("event-lock: true\n"); } if (loops > 1) { sb.Append("loops: " + loops + "\n"); } if (async) { sb.Append("async: true\n"); } if (applicationArguments != null) { sb.AppendFormat("content-type: text/plain\ncontent-length: {0}\n\n{1}\n", applicationArguments.Length, applicationArguments); } var tcs = new TaskCompletionSource <ChannelEvent>(); var subscriptions = new CompositeDisposable(); if (cts.Token.CanBeCanceled) { subscriptions.Add(cts.Token.Register(() => tcs.TrySetCanceled())); } subscriptions.Add( ChannelEvents.Where( x => x.EventName == EventName.ChannelExecuteComplete && x.Headers["Application-UUID"] == applicationUUID) .Take(1) .Subscribe( executeCompleteEvent => { if (executeCompleteEvent != null) { Console.WriteLine("{0} ChannelExecuteComplete [{1} {2} {3}]".Fmt( executeCompleteEvent.UUID, executeCompleteEvent.AnswerState, executeCompleteEvent.Headers[HeaderNames.Application], executeCompleteEvent.Headers[HeaderNames.ApplicationResponse])); } else { Console.WriteLine("No ChannelExecuteComplete event received for {0}".Fmt(application)); } tcs.TrySetResult(executeCompleteEvent); }, ex => tcs.TrySetException(ex), subscriptions.Dispose)); SubscribeEvents(EventName.ChannelExecuteComplete).ContinueWith(t => { if (t.IsCompleted) { SendCommand(StringBuilderPool.ReturnAndFree(sb)) .Then(reply => { if (!reply.Success) { tcs.TrySetResult(null); } }) .ContinueOnFaultedOrCancelled(tcs, subscriptions.Dispose); } else { tcs.TrySetException(t.Exception); } }); return(tcs.Task); }