Cookie CSS

Friday, May 8, 2015

Understanding UCMA Back to Back Calls Part II

Last week I talked about, and described the Back2Ball call class in UCMA.  This week we are going take a closer look at how to use this class to achieve advanced pbx capabilities in Lync/Skype for Business.

A practical example.  Let's say my company has a help-desk of agents.  Customers call in for help, the available agents answer the call, and all is well.... except....

With Lync/Skype4b another  L/S user as a customer would be exposed to the agents uri via a normal response group setting.  So what is to stop the customer from not waiting in line next time they need help and just dialing support agents directly and disrupting other sessions they are assigned to?  With the Back2Back call class we can answer a call, turn it into a conference, then invite support agents to join, all while showing the customer a generic uri and hiding the agents true identities from them.  So what does this look like?  In this example I used Melissa Wendt to call a UCMA endpoint, which then creates the conference and sends me a request to join.  When I do you can see that even though Melissa and I are on the same server she can't tell it's me.

Melissa's View



My View






































So what we have accomplished here is we have allowed me to answer Melissa's support call, help her, all with preserving my direct call details from her view.  So the goal for today is help you understand how we achieved this, or at least the first steps.

Step 1
Create a UMCA application, with an application endpoint and register an event for incoming calls.


private void incomingAVCall_CallReceived(object sender, CallReceivedEventArgs<AudioVideoCall> e)
{

string _destinationSipUri = "sip:yourdestination@yourdomain.com";

CallReceivedEventArgs<AudioVideoCall> args = e;

            _logger.Log("Incoming call.");

            AudioVideoCall incomingCall = args.Call;

            if (args.CallToBeReplaced == null)
            {
                _logger.Log("Receiving new call...");

                if (_useConference)
                {
                    // Create a new ConferenceCallSession to handle the call.
                    ConferenceCallSession newSession = new ConferenceCallSession(args.Call, _destinationSipUri, _logger, _appEndpoint);
                    newSession.Start();
                }
                else
                {
                    // Create a new CallSession to handle the call.
                    CallSession newSession = new CallSession(args.Call, _destinationSipUri, _logger);
                    newSession.Start();
                }
            }
            else
            {
                if (args.CallToBeReplaced.ApplicationContext is CallSession)
                {
                    // This is a self-transfer from a CallSession.
                    // Let the CallSession pick up where it left off.
                    _logger.Log("Receiving self-transfer...");
                    CallSession originalCallSession = (CallSession)args.CallToBeReplaced.ApplicationContext;
                    originalCallSession.HandleSelfTransfer(args.Call);
                }
                else if (args.CallToBeReplaced.ApplicationContext is ConferenceCallSession)
                {
                    // This is a self-transfer from a CallSession.
                    // Let the CallSession pick up where it left off.
                    _logger.Log("Receiving self-transfer for In...");
                    ConferenceCallSession originalCallSession = (ConferenceCallSession)args.CallToBeReplaced.ApplicationContext;
                    originalCallSession.HandleSelfTransfer(args.Call);
                }
                else if (args.CallToBeReplaced.ApplicationContext is ConferenceCallOutDial)
                {
                    // This is a self-transfer from a CallSession.
                    // Let the CallSession pick up where it left off.
                    _logger.Log("Receiving self-transfer for Out...");
                    ConferenceCallOutDial originalCallSession = (ConferenceCallOutDial)args.CallToBeReplaced.ApplicationContext;
                    originalCallSession.HandleSelfTransfer(args.Call);
                }
                else
                {
                    // This is not a proper self-transfer; decline.
                    args.Call.Decline();
                    _logger.Log("Declined invalid transfer.");
                }

            }

}

I know what you are thinking, where did all these other classes come from, and how am I reading this ApplicationContext?  In order to use Back2Back we need to get the call in the proper state using something called a self transfer.  Those methods allow us to do just that.

Step 2 Impersonation

So how to we impersonate our support group on the outside end of the call?

string realcallNum = "";
            string realcallUri = "";
            string realcallName = "";

            try
            {
                realcallUri = e.Call.Conversation.RemoteParticipants[0].Uri;
                realcallName = e.Call.Conversation.RemoteParticipants[0].DisplayName;
                realcallNum = e.Call.Conversation.RemoteParticipants[0].PhoneUri;
            }
            catch { }

            try
            {
                if (e.RequestData != null)
                {
                    e.Call.Conversation.Impersonate("sip:support@bridgeoc.com", null, "Bridge OC Support");

                }
            }

            catch { }

Step 3 - First Time Call

You probably noticed if it's a first time call in our example it'll get caught here.

if (args.CallToBeReplaced == null)
            {
                _logger.Log("Receiving new call...");

                if (_useConference)
                {
                    // Create a new ConferenceCallSession to handle the call.
                    ConferenceCallSession newSession = new ConferenceCallSession(args.Call, _destinationSipUri, _logger, _appEndpoint);
                    newSession.Start();
                }
                else
                {
                    // Create a new CallSession to handle the call.
                    CallSession newSession = new CallSession(args.Call, _destinationSipUri, _logger);
                    newSession.Start();
                }
            }

So let's take a look at our ConferenceCallSession and see what happens when we call the start method.


          _call.StateChanged +=
                new EventHandler<CallStateChangedEventArgs>
                    (OnCallStateChanged);

            // Accept the incoming call.
            try
            {
                _logger.Log("Accepting call...");

                _call.BeginAccept(
                    ar =>
                    {
                        try
                        {
                            _call.EndAccept(ar);
                            _logger.Log("Accepted call.");

                            while (_call.Flow == null)
                            {
                                Thread.Sleep(10);
                            }

 _call.ApplicationContext = this;

                                            // Self-transfer the call.
                                            try
                                            {
                                                _logger.Log("Self-transferring call...");

                                                _call.BeginTransfer(_call,
                                                    ar2 =>
                                                    {
                                                        try
                                                        {
                                                            _call.EndTransfer(ar2);

                                                            _logger.Log(
                                                                "Completed self-transfer.");
                                                        }
                                                        catch (RealTimeException ex)
                                                        {
                                                            _logger.Log("Self-transfer failed.",
                                                                ex);
                                                        }
                                                    },
                                                    null
                                                );
                                            }
                                            catch (InvalidOperationException ex)
                                            {
                                                _logger.Log("Failed initiating self-transfer.",
                                                    ex);
                                            }

So what this does it answer the call and then return it to the original code with the context set, and the call in the proper state to use the B2B calls calls.

Next week we'll delve into how to go from there, and how to setup the back2back call.

Doug Routledge, C# Lync, Skype for Business, SQL, Exchange, UC Developer  BridgeOC
Twitter - @droutledge @ndbridge


No comments:

Post a Comment

Any spam comments will be deleted and your user account will be disabled.