Edit

Share via


How to: Create a chat room add-in

Learn how to create a chat room add-in that displays Microsoft Unified Communications Managed API (UCMA) bot messages and filters a local user’s messages before they are posted to the chat room.

Applies to: Lync 2013

In this article
Code example: Chat room message filter

Prerequisites
Associate an add-in with a chat room
Creating a Silverlight add-in
Getting the hosting chat room
Next steps
Additional resources

Code samples


Lync 2013: Filter room messages before they are posted

Prerequisites

Before you create a chat room add-in, you need to meet the following prerequisites:

  • Microsoft Lync 2013 is installed and running on the development computer.

  • You have sign-in credentials for Microsoft Lync Server 2013.

  • Microsoft Lync 2013 SDK is installed on the development computer.

  • The Persistent chat role is enabled on the Lync Server 2013 topology that you sign in to.

  • You have the permission to associate an add-in with a chat room.

  • Microsoft Visual Studio or an express version of Microsoft Visual Studio is installed on your development computer.

Core concepts to know

Understanding the following concepts is essential to creating a chat room add-in.

Topic

Description

Chat room add-in application in Lync SDK

Learn about the Microsoft Lync 2013 Persistent Chat room add-in feature through the scenarios in which the add-in is used and the classes and methods that are used to create an add-in.

Chat rooms

Explains what a followed room is and how you use it in your application.

Room manager

Explains the role of the room manager in finding Persistent Chat rooms.

Chat room messages

Explains how room messages are encapsulated in the Lync 2013 API.

Create an add-in for the Persistent Chat window

Learn about designing a Persistent Chat room add-in that displays messages that a UCMA bot has posted to the hosting chat room and then filters the pending message posts of the local user.

Associate an add-in with a chat room

Before your add-in can be opened in a chat window, you need to register the add-in with the Lync Server 2013. The add-in is opened in the persistent chat room window when a user double-clicks a persistent chat room from the contact list. You can also register a persistent chat room by using a server-side tool such as the Group Chat Administration Tool. For information about associating an add-in with a chat room using the Microsoft Lync Server 2013 Persistent Chat SDK, see ChatRoomManagementServices.BeginUpdateChatRoom.

TipTip

You should associate your new add-in with a private chat room so that you can test the add-in. When your add-in is ready for public use, associate it with a public chat room.

A private chat room is only visible to its members. This means that the private chat room cannot be found by using a chat room query to search for a chat room.

Creating a Silverlight add-in

Use Microsoft Visual Studio to create the solution and project files for the add-in. You can open the solution in Microsoft Expression Web if you want to create an enhanced user experience.

To create a Silverlight add-in

  1. Start Microsoft Visual Studio and click New Project… on the start page.

  2. In the New Project dialog, select Templates -> Visual C# -> Silverlight.

  3. Select the Lync Silverlight Application template in the middle pane of the New Project dialog. This template populates the references folder with the assembly that contains the namespace you use to get chat room features.

  4. Set the name, location, and solution name for your solution and click the OK button.

  5. In the New Silverlight Application dialog, un-check Host the Silverlight application in a new Web site.

  6. In the Options group, select Silverlight 5 from the Silverlight Version list and click the OK button.

The solution file is now ready to update. The MainPage.xaml file declares a user control that nests a grid, stack panel, and the PresenceIndicator control. This control is in the stack panel to show you how to embed Lync Controls in your Silverlight application.

Getting the hosting chat room

The hosting chat room is the entry point to all Lync functionality that you can code in an add-in. To get the room, call the LyncClient.GetHostingRoom method. The best place to get the hosting chat room is in the add-in constructor.

To get the hosting chat room

  1. In the constructor for your page, add a try/catch block for the Microsoft.Lync.Model.LyncClientException class.

  2. Get the Microsoft.Lync.Model.Room.Room object that encapsulates the hosting chat room by calling the static [M:Microsoft.Lync,Model.LyncClient.GetHostingRoom] method.

  3. Enable message filtering by calling the Room.EnableOutgoingMessageFilter method.

  4. Register for the Room.IsSendingMessage event so you can catch and filter messages before they are posted by the local user.

  5. Register for the Room.MessagesReceived event so you can catch new messages after they are posted to the hosting chat room.

The following example gets the hosting room, enables outgoing message filtering, and registers for messaging events.

        /// <summary>
        /// Constructor of the add-in page
        /// </summary>
        public MainPage()
        {
            InitializeComponent();
 
            try
            {
                //Get the hosting chat room
                _room = (Room)LyncClient.GetHostingRoom();
                if (_room != null)
                {

                    //Tell Lync platform that outgoing messages are to filtered and formatted 
                    //If outgoing message filtering is not enabled then enable it
                    if (_room.IsOutgoingMessageFilterEnabled == false)
                    {
                        _room.EnableOutgoingMessageFilter();
                    }

                    //Register for message events 
                    _room.IsSendingMessage += new EventHandler<RoomMessageEventArgs>(_room_IsSendingMessage);
                    _room.MessagesReceived += new EventHandler<RoomMessagesEventArgs>(_room_MessagesReceived);
 
                }
            }
            catch (LyncClientException lyncClientException)
            {
                Debug.WriteLine(lyncClientException);
            }
            catch (SystemException systemException)
            {
                if (IsLyncException(systemException))
                {
                    // Log the exception thrown by the Lync Model API.
                    Console.WriteLine("Error: " + systemException);
                }
                else
                {
                    // Rethrow the SystemException which did not come from the Lync Model API.
                    throw;
                }
            }
 
        }

Code example: Chat room message filter

The following example uses XAML to declare the add-in UI. The controls include an entry field that the user types a filter string into, and text blocks that show the original message post and the post after the add-in has reformatted it.

<UserControl x:Class="FilterMessageAddIn.FilterMessageAddIn"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="28*" />
            <RowDefinition Height="119*" />
            <RowDefinition Height="153*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="143*" />
            <ColumnDefinition Width="257*" />
        </Grid.ColumnDefinitions>
        <TextBlock Name="PendingPost" Text="TextBlock" Grid.Column="1" Grid.Row="1" Height="131"  VerticalAlignment="Top" HorizontalAlignment="Left" Width="245" Grid.RowSpan="1" />
        <TextBlock Name="UpdatedPost" Text="TextBlock" Height="122" Grid.Row="2" Grid.Column="1"   Margin="0,20,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="245" />
        <sdk:Label Name="PendingPost_Label" Height="18 " Width="121" Content="Pending Post" Margin="12,12,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.RowSpan="1" Grid.Column="0" Grid.Row="1" /> 
        <sdk:Label Name="UpdatedPost_Label"  Grid.Column="0"  Grid.Row="2"  Height="28"  HorizontalAlignment="Left"  VerticalAlignment="Top" Width="120" Content="Updated Post"  Margin="12,20,10,114"/>
        <sdk:Label Name="FilterAction" Grid.Column="0"   Grid.ColumnSpan="1"  Grid.Row="2"  Height="28" HorizontalAlignment="Left" Margin="0,74,0,0"  VerticalAlignment="Top" Width="147" />
        <sdk:Label Name="FilterText_label" Content="Filter Text" Height="21" HorizontalAlignment="Left" Margin="12,12,0,0"  VerticalAlignment="Top" Width="140"  />
        <TextBox Grid.Column="1" Grid.Row="0" Grid.RowSpan="1" Height="23" HorizontalAlignment="Left" Margin="1,1,0,0" Name="FilterTextString" VerticalAlignment="Top" Width="120" />
    </Grid>
</UserControl>

The following example interacts with the XAML user control of a Silverlight chat room add-in. When the user posts a message to the hosting chat room, this add-in catches the message before it's posted. If the message contains the filter string that is entered in the filter text box, the message post is canceled. Otherwise, the message is posted to the room

using System;
using System.Windows.Controls;
using Microsoft.Lync.Model;
using Microsoft.Lync.Model.Room;

namespace FilterMessageAddIn
{
    public partial class FilterMessageAddIn : UserControl
    {
        /// <summary>
        /// The hosting group chat room
        /// </summary>
        Room _HostingRoom;

        /// <summary>
        /// Class constructor
        /// </summary>
        public FilterMessageAddIn()
        {
            InitializeComponent();

            try
            {
                //Get the group chat room that is hosting this add-in
                _HostingRoom = LyncClient.GetHostingRoom();
                if (_HostingRoom != null)
                {
                    //If the hosting room outgoing message filter is not enabled then enable it.
                    if (_HostingRoom.IsOutgoingMessageFilterEnabled == false)
                    {
                        _HostingRoom.EnableOutgoingMessageFilter();
                    }

                    //Register for outgoing message post events.
                    _HostingRoom.IsSendingMessage += new EventHandler<RoomMessageEventArgs>(_HostingRoom_IsSendingMessage);
                }
            }
            catch (ClientNotFoundException cnf)
            {
                System.Diagnostics.Debug.WriteLine("The Lync client is not running:" + cnf.Message);
            }
            catch (LyncClientException lce)
            {
                System.Diagnostics.Debug.WriteLine("Lync client exception on open add-in:" + lce.Message);
            }
        }


        /// <summary>
        /// Invoked when a local user is posting a message to the hosting chat room
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _HostingRoom_IsSendingMessage(object sender, RoomMessageEventArgs e)
        {

            /// <summary>
            /// The message filtering action taken by the application after filter/format logic is run
            /// </summary>
            RoomMessageFilteringAction messageAction;


            //Get the pending message
            RoomMessage messageToFilter = e.Message;

            //Extract the plain-text version of the message.
            string pendingPostText = e.Message.MessageDictionary[RoomMessageFormat.PlainText].ToString();
             
            //Update UI with the text of the pending message.
            this.Dispatcher.BeginInvoke(new ShowPendingMessageDelegate(ShowPendingMessage), new object[] { pendingPostText });

            //Check message string to see if the message contains the sub-string entered on the add-in UI
            if (FilterMessagePost(pendingPostText, FilterTextString.Text))
            {
                //The message string contains the sub-string so the message action is canceled. 
                //Update the UI with the message filtering action and do not update the UpdatedMessage text block.
                this.Dispatcher.BeginInvoke(new ShowFilterMessageActionDelegate(ShowFilterMessageAction), new object[] { RoomMessageFilteringAction.Canceled });
                messageAction = RoomMessageFilteringAction.Canceled;
            }
            else
            {

                //The message string DOES NOT contain the filter sub-string.

                //If the pending message starts with "do not reformat" in any letter case then 
                //update the UI with the message filter action, update the UI updated message box with the original message text,
                //and set the message filtering action to Passed
                if (pendingPostText.ToUpper().StartsWith("DO NOT REFORMAT:"))
                {
                    this.Dispatcher.BeginInvoke(new ShowUpdatedMessageDelegate(ShowUpdatedMessage), new object[] { pendingPostText });
                    this.Dispatcher.BeginInvoke(new ShowFilterMessageActionDelegate(ShowFilterMessageAction), new object[] { RoomMessageFilteringAction.Passed });
                    messageAction = RoomMessageFilteringAction.Passed;

                }
                else
                {
                    //Reformat the original message text by setting all characters to upper case.
                    //Update the UI with the message filter action, update the UI updated message box with the reformatted message text
                    //and set the message filtering action to Replaced
                    this.Dispatcher.BeginInvoke(new ShowUpdatedMessageDelegate(ShowUpdatedMessage), new object[] { FormatMessagePost(pendingPostText) });
                    this.Dispatcher.BeginInvoke(new ShowFilterMessageActionDelegate(ShowFilterMessageAction), new object[] { RoomMessageFilteringAction.Replaced });
                    messageAction = RoomMessageFilteringAction.Replaced;
                }
            }

            //Update the pending message with the updated message text UI text block
            messageToFilter.MessageDictionary[RoomMessageFormat.PlainText] = UpdatedPost.Text;

            //Send the filtered message with whatever message filtering action was set in the previous code.
            _HostingRoom.SendFilteredMessage(messageToFilter, messageAction);
        }


        /// <summary>
        /// Message filter method searches the pending message text for the sub-string entered
        /// on the main UI. If the substring is found, true is returned.  Otherwse, false is returned.
        /// true = message is filtered and must be canceled.
        /// false = message passes filter and can be posted.
        /// </summary>
        /// <param name="pendingMessagString"></param>
        /// <returns></returns>
        private Boolean FilterMessagePost(string pendingMessagString, string messageFilter)
        {
            if (pendingMessagString.Contains(messageFilter))
            {
                //Cancel message.
                return true;
            }
            //Post message.
            return false;
        }
        
        /// <summary>
        /// Message text formatter. sets all message text to upper case.
        /// This simplistic example works well for plain-text messages. If your application will 
        /// format rtf or Html messages, be sure to apply equivalent formatting logic to all formats of a message's text before 
        /// posting.
        /// </summary>
        /// <param name="pendingMessageString"></param>
        /// <returns></returns>
        private string FormatMessagePost(string pendingMessageString)
        {
            return pendingMessageString.ToUpper();
        }


        private delegate void ShowPendingMessageDelegate(string pendingMessage);

        /// <summary>
        /// Updates the main UI pending message block
        /// </summary>
        /// <param name="pendingMessage"></param>
        private void ShowPendingMessage(string pendingMessage)
        {
            PendingPost.Text = pendingMessage;
        }

        private delegate void ShowUpdatedMessageDelegate(string updatedMessage);
        /// <summary>
        /// Updates the main UI updated message text block with the reformatted message text.
        /// </summary>
        /// <param name="updatedMessage"></param>
        private void ShowUpdatedMessage(string updatedMessage)
        {
            UpdatedPost.Text = updatedMessage;
        }

        private delegate void ShowFilterMessageActionDelegate(RoomMessageFilteringAction action);
        /// <summary>
        /// Updates the main UI filter action label content with the action to be applied to the
        /// pending message post.
        /// </summary>
        /// <param name="action"></param>
        private void ShowFilterMessageAction(RoomMessageFilteringAction action)
        {
            FilterAction.Content = action.ToString();
        }
      
    }
}

Next steps

Now that you have created a chat room add-in, learn about how to make your add-in interact with the hosting chat room.

See also