Microsoft Lync and Skype for Business have a rich set of .NET APIs which make it easy to extend the platform and integrate it with other applications. This blog helps explain how to use those APIs.

Sending a call to a specific endpoint in UCMA

Posted: March 2nd, 2012 | Author: | Filed under: UCMA 3.0 | Tags: | 6 Comments »

One of the cool things about Lync that people often take for granted is that you can sign in from multiple computers (or devices), and any call that is sent to you will ring at all of those various locations. There’s quite a bit of complex routing logic that goes into this, but it mostly takes place under the covers, and even when you’re developing a UCMA application, you generally don’t need to worry about it. If you create a new AudioVideoCall object and call AudioVideoCall.BeginEstablish with a SIP URI, Lync automatically “forks” that call to all of the registered endpoints for that user. Basically when the call hits the Lync Front End Server, the Front End Server sends a branch to each of the endpoints where that user is signed in. The first one to answer gets to take the call.

While it’s very courteous of Lync Server to do all of this stuff on its own without bothering us, the branching behaviour can sometimes get in the way. You might want to send an IM or a call to a user at one specific location. Maybe you want a call to go only to the user’s IP desk phone, but not to the Lync client on the PC. What do you do in a case like this?

If you read my previous post on GRUUs, and you were wondering what possible use anyone could get out of one of these creatures, the time has now come. Lync has a particular type of GRUU that it uses to identify a single endpoint — that is to say, not just a Lync user, but a specific location (IP address, port) where that user is signed in. Here’s what one looks like:

sip:michael@domain.local;opaque=user:epid:tA_uNyaIa52myaQiU_MRzAAA;gruu

If you use this GRUU as the destination URI when establishing a call, it will go only to that single endpoint, bypassing the fancy forking logic applied by Lync!

Now, how do you find out an endpoint URI like this to use for calls? Well, anywhere you have access to a ParticipantEndpoint object in UCMA, you can check the ParticipantEndpoint.Uri property to get the GRUU for that endpoint. For example, in a two-party call, you can check Call.RemoteEndpoint.Uri. In a conference, you can get the collection of remote participants using the GetRemoteParticipantEndpoints method, and get endpoint URIs from the collection of ParticipantEndpoint objects you get back.

To give you a brief and very simple demonstration, I’ve written up a quick sample app. This is what it does: when you place an audio call to the application, it accepts the call and then sends you an IM at the specific endpoint where you called from. Sign in from two or more computers and give it a try. (Please forgive the slightly messy state of the code and the lack of error handling.)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Collaboration.AudioVideo;
using System.Threading;
using Microsoft.Rtc.Signaling;

namespace EndpointUriSample
{
    class Tester
    {
        CollaborationPlatform _platform;
        ApplicationEndpoint _endpoint;
        AudioVideoCall _avCall;

        public void Start()
        {
            ProvisionedApplicationPlatformSettings settings = new ProvisionedApplicationPlatformSettings("tester", "urn:application:mgreenlee.test");

            _platform = new CollaborationPlatform(settings);

            _platform.EndStartup(_platform.BeginStartup(null, null));

            ApplicationEndpointSettings endpointSettings = new ApplicationEndpointSettings("sip:mgreenlee.test@ccdev.claritycon.com",
                "ccdev-lync-001.ccdev.clarityinternal.net", 5061);

            _endpoint = new ApplicationEndpoint(_platform, endpointSettings);

            _endpoint.EndEstablish(_endpoint.BeginEstablish(null, null));

            _endpoint.RegisterForIncomingCall<AudioVideoCall>(OnAudioVideoCallReceived);
        }

        void OnAudioVideoCallReceived(object sender, CallReceivedEventArgs<AudioVideoCall> e)
        {
            e.Call.BeginAccept(
                ar =>
                {
                    try
                    {
                        e.Call.EndAccept(ar);

                        PlaceInstantMessageCallToSender(e.Call.RemoteEndpoint.Uri);
                    }
                    catch (RealTimeException ex)
                    {
                        Console.WriteLine(ex);
                    }
                },
                null);
        }

        private void PlaceInstantMessageCallToSender(string senderEndpointUri)
        {
            Conversation conversation = new Conversation(_endpoint);
            InstantMessagingCall imCall = new InstantMessagingCall(conversation);

            imCall.BeginEstablish(senderEndpointUri, null,
                ar =>
                {
                    try
                    {
                        imCall.EndEstablish(ar);

                        SendInstantMessage(imCall);
                    }
                    catch (RealTimeException ex)
                    {
                        Console.WriteLine(ex);
                    }
                },
                null);
        }

        private static void SendInstantMessage(InstantMessagingCall imCall)
        {
            imCall.Flow.BeginSendInstantMessage("You are the endpoint that called me.",
                ar2 =>
                {
                    try
                    {
                        imCall.Flow.EndSendInstantMessage(ar2);
                    }
                    catch (RealTimeException ex)
                    {
                        Console.WriteLine(ex);
                    }
                },
                null);
        }

        public void Stop()
        {
            _endpoint.EndTerminate(_endpoint.BeginTerminate(null, null));

            _platform.EndShutdown(_platform.BeginShutdown(null, null));
        }

    }
}

You’ll just need to add a Program.cs with a Main method that instantiates the class and calls the Start method. Feel free to comment or email me if you have any questions!


6 Comments on “Sending a call to a specific endpoint in UCMA”

  1. 1 How to tell where a call is ringing in UCMA | Lync Development said at 12:31 pm on May 8th, 2012:

    […] message tells you the GRUU of the specific endpoint where the call is ringing, which you can use to direct messages to that exact endpoint. You can also look at the User-Agent header to find out what type of device the call is ringing on, […]

  2. 2 Eric McKerrow said at 1:13 pm on May 14th, 2012:

    Hi Michael,
    I’ve worked through this sample and have a similar example. My question is this:
    Can I accept an Instant Message call and respond in the same conversation using the same EndPoint?
    Thanks, Eric

  3. 3 Michael said at 1:28 pm on May 14th, 2012:

    Hi Eric,
    Sorry, I’m not exactly sure what you mean. Are you asking about responding with an audio call, or sending IMs back using the same InstantMessagingCall?
    Thanks,
    Michael

  4. 4 Eric McKerrow said at 2:22 pm on May 14th, 2012:

    Hi Michael,
    I am trying to send IM’s back using the same InstantMessagingCall.

    This is what I’d like: I’d like to wait for someone to send me an IM, receive the IM and respond to the IM so the user would appear to be having a conversation with me (without creating a new a new window). I currently seem to only be able to start a new conversation. i.e. a user sends me a message, it opens on my machine , then I send them a new message that opens on their screen as a different conversation from me …. which is confusing for the person who sent me the message because another IM window opens on their machine. I’m sort of trying to create a bot that will respond based on some FAQ’s
    Thanks Eric

  5. 5 Michael said at 7:32 pm on May 18th, 2012:

    Hi Eric,
    After accepting the incoming IM using InstantMessagingCall.BeginAccept, you should be able to respond with IMs by calling InstantMessagingCall.Flow.BeginSendInstantMessage. Normally that should keep the messages in the same window. Is that what you’re doing right now?
    Michael

  6. 6 Mario said at 10:07 am on August 31st, 2015:

    Hello Michael.
    Could you share with us the manifest file that you used for this example please. I need to know how did the proxy and see how you send the flow to specific GRUU.

    Thank you very much Greetings!


Leave a Reply

  • Note: Comment moderation is in use because of excessive spam. Your comment may not appear immediately.

  •