Sunday, May 10, 2015

CTI integration between CRM USD and Microsoft Lync - Part 1

Last year, Microsoft released USD (Unified Service Desk) for CRM 2013. USD is a framework to build contact centre applications to provides a unified view of customer data stored in CRM and other applications.
It provides the adapters and modules to manipulate CRM UI elements, agent scripting, configuration toolbars and a CTI adapter.  For more information on USD please check https://technet.microsoft.com/en-us/library/dn646899(v=crm.6).aspx
For this tutorial, we are only interested in CTI adapters. The following 2 types of adapters can be used to integrate USD with CTI systems.
  • Generic listener adapter:
    USD provides this adapter out of the box. This adapter listens for HTTP request on a port 5000 ( http://localhost:5000/). On receiving a request, the generic listener adapter extracts a query string from the URL and uses the query string as parameters to raise a CTI screen pop up in USD.


  • Custom CTI adapter:
    User Interface Integration(UII) CTI framework provides the component to build custom CTI adapters. The customer adapter can provide a lot of functionality to manage calls and agent state. Here is the link on how to build a custom CTI adapter for USD.

  • We will be using the generic listener adapter to achieve USD and Lync integration.

    Prerequisite

    1. Access to CRM2013 (I m using a on premise CRM)
    2. Lync client installed on the development machine 
    3. Microsoft Lync  2013 SDK installed on the development machine (Download)
    4. USD (Unified Service Desk) installed on the development machine
      The following article lists the step by step instructions to install USD.
      https://technet.microsoft.com/en-au/library/dn646908.aspx.  The USD installation involves:
      • Installing USD client (UnifiedServiceDesk.msi)
      • Deploying USD Solution using Package Deployer (CRM2013-USD-PackageDeployer.exe) For this tutorial, deploy “CRM 2013 SP1” solution

    Process Flow Diagram


    image
    1. Customer makes a phone call
    2. Phone call pop up appears in the Lync Client
    3. A custom middleware application receives the conversation event and posts a HTTP request on “http://localhost:5000” with customer phone number.
      http://localhost:5000/?phone=+61456564323
    4. USD generic listener pickup the event, extract the query parameter and use these parameters to pop contact record

    Configuring the generic listener adapter in CRM

    Make sure that all the prerequisite steps have been completed
    1. Logon to CRM  and navigate to “Settings>>Hosted Controls”
    2. Click “New” enter the fields as shown in the screen shot below

      image
      USD component, Display Group,Assembly URL and Assembly Type are the important fields and should contain the exactly same values.
      Assembly URL : Microsoft.Crm.UnifiedServiceDesk.GenericListener
      Assembly Type: Microsoft.Crm.UnifiedServiceDesk.GenericListener.DesktopManager
    3. Save the record.

    Creating a Lync Connector (Middleware application)

    A Lync connector application will capture the conversation event of the Lync client when a phone call is made or received.  This application is a client side application.
    I am creating the Lync connector as a WPF application.  I don’t need a user interface for the connector for this blog but may use it add more functionality to it in future.
    1. Create a new WPF application project and name it “LyncConnector”.

      image
    2. Add “Microsoft.Lync.Model.dll” as reference to the project. The file will be available in the following location.
      \Microsoft Lync\SDK\Assemblies\Desktop

      image
    3. The following code is going in the “MainWindow.xaml.cs file.
      Code:
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using System.Windows;
      using System.Windows.Controls;
      using System.Windows.Data;
      using System.Windows.Documents;
      using System.Windows.Input;
      using System.Windows.Media;
      using System.Windows.Media.Imaging;
      using System.Windows.Navigation;
      using System.Windows.Shapes;
      using Microsoft.Lync.Model;
      using Microsoft.Lync.Model.Conversation;
      using System.Net;
      using System.Text.RegularExpressions;
      using System.Collections.Specialized;
      
      namespace LyncConnector
      {
          /// <summary>
          /// Interaction logic for MainWindow.xaml
          /// </summary>
          public partial class MainWindow : Window
          {
              private LyncClient _lyncClient;
      
              public MainWindow()
              {
                  InitializeComponent();
      
                  //Get the link client- Make sure Lync client is running
                  _lyncClient = LyncClient.GetClient();
      
                  // Invoke the conversation event when the conversation starts in Lync Client
                  _lyncClient.ConversationManager.ConversationAdded +=
                      new EventHandler<Microsoft.Lync.Model.Conversation.ConversationManagerEventArgs>(
                          OnConversationAdded);
      
              }
              void OnConversationAdded(object sender, Microsoft.Lync.Model.ConversationConversationManagerEventArgs e)
              {
                  // Get the properties for this conversation.
                  IDictionary<ConversationProperty, object> properties = e.Conversation.Properties;
      
                  // Get the Contact object for the person initiating the conversation.
                  Contact inviterContact = properties[ConversationProperty.Inviter] as Contact;
      
                  // Get the URI of the inviter.
                  string inviterUri = inviterContact.Uri;
      
                  // Try to match a phone number in the URI.
                  Regex phoneRegex = new Regex(@"\+(\d+)");
                  Match phoneMatch = phoneRegex.Match(inviterUri);
      
                  string callerId = string.Empty;
                  
                  if (phoneMatch.Success)
                  {
                      // Load the data only if the caller is from a PSTN number.
                      callerId = phoneMatch.ToString();
                      LoadDataForCaller(callerId);
                  }
              }
              private void LoadDataForCaller(string callerId)
              {
                            
                  string UsdLocalAddress = "http://localhost:5000";
      
                  using (var wb = new WebClient())
                  {
                      var data = new NameValueCollection();
                      
                      // By default generic listener pass the follwing 3 parameters to USD/
                      //You can use them to post some data. If you don't use them then generic listener will still post the empty values for these parameters. 
      
                      /*
                      data["ani"] = Ani;
                      data["dnis"] = Dnis;
                      data["type"] = CallType;
                       */
                      
                      data["telephone1"] = callerId;
      
      
                      try
                      {
                          if (Uri.IsWellFormedUriString(UsdLocalAddress, UriKind.RelativeOrAbsolute))
                              wb.UploadValues(new Uri(UsdLocalAddress), "POST", data);
                      }
                      catch (SystemException ex)
                      {
                          //System.Windows.MessageBox.Show(ex.Message, "Failed to send call event");
                      }
                  }
                 
              }
          }
      }
      

    4. Compile  and Run the code



    Test the integration



    1. Make sure Lync client is running
    2. Make sure Lync Connector application is running.
    3. Logon to USD and click on the setting >>Debug

      image

    4. Delete all the entries in the “Action Calls” tab by clicking the delete button as shown in the screen shot below.

      image

    5. Make a phone call or ask someone to ring you on Lync. If everything is working properly,  a new event will appear in Action Calls tab as shown in the screen shot.

       image



    So here you go, we have successfully passed the caller's phone number to USD.
    That is it for today. In the next tutorial we will use this phone number to pop up the contact record.

    6 comments:

    1. Hi,

      When I use the code you mentioned, I get the following errors:

      For this line of code:

      // Invoke the conversation event when the conversation starts in Lync Client
      _lyncClient.ConversationManager.ConversationAdded +=
      new EventHandler(
      OnConversationAdded);

      I get this error:

      No overload for 'OnConversationAdded' matches delegate 'EventHandler'

      For this line of code:

      // Get the properties for this conversation.
      IDictionary properties = e.Conversation.Properties;

      I get this error:

      Using the generic type 'IDictionary' requires 2 type arguments

      How do I go about resolving these errors.

      ReplyDelete
      Replies
      1. For the first bit, replace that code with this:

        // Invoke the conversation event when the conversation starts in Lync Client
        _lyncClient.ConversationManager.ConversationAdded += OnConversationAdded;

        for the second bit, just delete those two lines you put in here, and you should be good to go!

        Delete
    2. Sorry guys. My formatting stuff up the CSharp code, specifically "<" and ">" were not visible. I have updated the code and the formatting.

      ReplyDelete
    3. Hello Amreek ,

      Does this adapter work For Skype For Business

      ReplyDelete
    4. Hello Amreek ,

      Do we have Automatic Call Distributor (ACD) features included with CTI under integration?

      ReplyDelete