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.

Transferring calls in UCMA 2.0, part one

Posted: April 14th, 2009 | Author: | Filed under: OCS Development, UCMA 2.0 | No Comments »

College students, like very small children, are easily entertained. Back in my college days, I always thought it would be fun to play with the campus PBX phone system by calling a classmate and then transferring the call to a random professor as soon as they answered the phone. The ensuing call would go something like this:

Student: Hello?image

<>

Professor: Hello?

Student: Hello?

Professor: Yes, this is Professor Wolford; who is this?

Student: I think you may have the wrong number…

Professor: Did you call me?

Student: I don’t think so; maybe it was someone else?

Professor: Then why are you on the phone? I’m not sure I understand.

Student: This is my phone. You called 4512, right?

Professor: You called me. At least, my phone rang.

Student: I’m sorry, I thought my phone rang…

Professor: Excuse me, I have a good deal of work to do. Please don’t call me again. Thank you.

<>

Now, if only I’d had UCMA 2.0 back in my college years, I could have pulled this off with an Office Communication Server deployment and a few well-chosen lines of code. I wouldn’t even have had to mess with pulsating dial tones and FLASH buttons.

There are many more practical uses for code-driven transferring of calls, and I’m going to show you a few ways of doing this using UCMA 2.0. If you use this knowledge to set up stupid phone pranks instead of creating an auto-dialer for your call center or placing calls on behalf of employees through your CRM system,I take no responsibility for the results.

As our starting point,we’ll use the template I gave you a few posts ago for the PresenceMonitor class. To get things off the ground, go ahead and change the SubscribeToPresence method to ExecuteTransfer.

private void EstablishCompleted(IAsyncResult result)
{
    _endpoint.EndEstablish(result);
    Console.WriteLine("Established endpoint.");

    ExecuteTransfer();
}

We’re also going to need a handful of new instance variables, so I’ll just give them to you now to paste somewhere around the top of your class. Leave the constants with server settings alone, and make sure you have the following for your private instance variables:

private string _mySipUri;
private string _yourSipUri;
private string _yourSipUri2;
private CollaborationPlatform _platform;
private UserEndpoint _endpoint;
private Conversation _conversation;
private AudioVideoCall _call;
private Conversation _conversation2;
private AudioVideoCall _call2;

Also, change the first part of the run method, where it collects SIP URIs, to take three instead of two:

Console.Write("Enter your SIP URI: ");
_mySipUri = Console.ReadLine();

Console.Write("Enter another SIP URI: ");
_yourSipUri = Console.ReadLine();

Console.Write("Enter a third SIP URI: ");
_yourSipUri2 = Console.ReadLine();

To kick things off, paste the following into your class for the ExecuteTransfer method. All we’ll do for now is create a new Conversation object and a new AudioVideoCall object associated with that conversation.

private void ExecuteTransfer()
{
    // Create a conversation and a call.
    _conversation = new Conversation(_endpoint);
    _call = new AudioVideoCall(_conversation);
}

At the bottom of the method, add another line of code to actually place the call. This particular override of the BeginEstablish method allows us to specify a single SIP URI with which we want to establish a one-on-one call (or peer-to-peer call, as we say in the biz).

_call.BeginEstablish(_yourSipUri, null, CallEstablishCompleted, null);

As you can see, we’ll need a callback method named CallEstablishCompleted. If you move really quickly, you may be able to paste it in before Visual Studio starts getting antsy and marking things with squiggly red lines. Hurry up!

private void CallEstablishCompleted(IAsyncResult result)
{
    _call.EndEstablish(result);

    // Execute an unattended transfer
    _call.BeginTransfer(_yourSipUri2, 
        new CallTransferOptions(CallTransferType.Unattended),
        TransferCompleted, null);
}

The first parameter of BeginTransfer is the SIP URI of the transfer target – the SIP URI to which we want to transfer the call. The second parameter is an instance of CallTransferOptions, which in turn takes a CallTransferType, in this case Unattended. The third and fourth parameters are the usual suspects, our callback delegate and state object.

We’re starting with the unattended transfer type because in a way it’s the most primitive. In an unattended transfer, Endpoint A says to Endpoint B, “Go talk to this other person,” and then hangs up without waiting to see that the transfer goes through. Endpoint A has no way of knowing whether the transfer was successful. Unattended transfer is for the risk-takers and daredevils among endpoints.

Last but not least, we need a callback method for the transfer operation. This one is straightforward.

private void TransferCompleted(IAsyncResult result)
{
    try
    {
        _call.EndTransfer(result);
    }
    catch (OperationFailureException ex)
    {
        Console.WriteLine("Couldn't complete the transfer.");
    }
    Console.WriteLine("Transfer initiated.");
}

Once you’ve plugged in this last callback method, go ahead and test out the code. (You’ll need to have a console application or something like that to start everything up; I’ll let you take care of that on your own.)

A couple of friendly reminders, since I always forget these myself: first, make sure to add sip: to the beginning of the SIP URIs you enter when testing, or things will break, and there won’t even be any exciting flying sparks. Second, if you have any weird issues, take a close look at the SIP URIs you entered. I can’t count the number of times I’ve spent a solid hour on fruitless debugging only to realize that, thanks to a misspelled configuration setting, my code was trying to contact someone at Clartiy Consulting instead of Clarity.

If all goes well, when you run the sample you will get an incoming call from your own SIP URI at the second SIP URI you entered. As soon as you answer the call, a new call window will pop up with an outgoing call to the third SIP URI, and the first call will hang up.

So far, so good.

Let’s try a subtle but important change. Go back to the CallEstablishCompleted method and change the CallTransferType to CallTransferType.Attended.

_call.BeginTransfer(_yourSipUri2, 
    new CallTransferOptions(CallTransferType.Attended),
    TransferCompleted, null);

Now run your program again and watch carefully what happens.

This time, when that second call window pops up with the transfer, the initial call will stay put while the transfer goes through, looking something like this:

image

It won’t hang up until the person at the third SIP URI answers the transferred call. It’s sort of like when you give someone a ride home, and you wait until they’ve gotten in the front door and waved goodbye before you drive away, just in case they forgot their keys and no one’s home.

If you don’t answer the transferred call, the EndTransfer method will throw an OperationFailureException with a message that the transfer couldn’t be completed, which we’re catching in the TransferCompleted method. At this point, you’ll be able to resume the first call if you like.

If you’ve even been on hold with a customer service agent, and they’ve said to you, “I’m going to see if so-and-so is available, and if she is, then I’ll transfer you; if not, I’ll be back,” then you’ve seen this process at work. With an attended transfer, your code can keep tabs on whether the transfer worked, and resume the initial call if it didn’t.

Next, we’re going to look at what is probably the coolest of the transfer types: the supervised transfer. It only works with OCS 2007 R2, and the clients you are calling must be using Office Communicator 2007 R2 for the transfer to go through properly. The supervised transfer involves something called call replacing.

The one issue we have with the transfers we’ve done so far is that we have to place the first call and then wait until the person answers before transferring them. This puts a bit of a damper on the prank – or, er, the call center auto-dialer. It would be nice if we could place both calls at once, and just transfer one into the other when we’ve got them both on the line.

So that’s exactly what we’re going to do.

Go back to your ExecuteTransfer method and change it so that it creates two conversations (_conversation and _conversation2) and two calls (_call and _call2). I’m sure you can do this yourself, but there’s an extra wrinkle we need to take care of, so here’s the code:

private void ExecuteTransfer()
{
    // Create a conversation and a call.
    _conversation = new Conversation(_endpoint);
    _call = new AudioVideoCall(_conversation);
    _conversation2 = new Conversation(_endpoint);
    _call2 = new AudioVideoCall(_conversation2);

    // Establish a call to the first SIP URI.
    _call.BeginEstablish(_yourSipUri, null, CallEstablishCompleted, _call);
    _call2.BeginEstablish(_yourSipUri2, null, 
        CallEstablishCompleted, _call2);
}

The important thing to note is that we’re passing the call we’re establishing into the BeginEstablish method as the state parameter. This will allow us to magically pull it out of the IAsyncResult in a moment. Stay tuned.

Here’s our brand new CallEstablishCompleted method. This one works a little differently, since it needs to deal with callbacks for two different calls. Notice how we’re getting the correct AudioVideoCall object from the AsyncState property on the IAsyncResult, so we can call EndEstablish on the right call.

private void CallEstablishCompleted(IAsyncResult result)
{
    AudioVideoCall call = result.AsyncState as AudioVideoCall;
    call.EndEstablish(result);

    if (_call.State == CallState.Established && 
        _call2.State == CallState.Established)
    {
        // Execute a supervised transfer
        _call.BeginTransfer(_call2, TransferCompleted, null);
    }
}

We also check to see if both calls are established; once we’ve got both of them up and running, we begin the transfer. We’re using a new and unfamiliar override of BeginTransfer here – this one takes three parameters, and the first one is another AudioVideoCall object. What’s going on here?

This supervised transfer works by adding a Replaces header to the SIP REFER message that tells the other endpoint to go talk to someone else. The Replaces header tells the endpoint to plug into a specific existing call, so that we can seamlessly bridge together two calls we’ve already established separately with different people.

Run the sample and see what happens. This time, you won’t see two different call windows when the transfer is in process. Instead, the call will magically turn into a call with a different person.

You now have several new tools at your disposal – unattended transfer, attended transfer, and supervised transfer – that may be used either for good or for evil. I trust you to make the right decisions.

The finished class, in all its glory, is here.

Check back soon for installment two, in which I delve into some even crazier territory with dialing out from conferences and a roundabout way to escalate AudioVideoCalls.



Leave a Reply

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

  •