Bridge Communications

Sunday, November 9, 2014

Popup Notification of New Lync Message

One of the few things that annoyed me about Lync when I first started using it, was I had issues noticing if I had received a new message. I have it configured so show all of my normal conversations in tabbed mode on the 5th of my 8 monitors I use for work during a normal day. I am also known to put on headphones for music, or receive voice calls during the business day that would prevent me from hearing the chip sound notification as well. Back then as a programmer just getting to know the Lync SDKs I decided this was a problem I could code my way out of, and to a somewhat extreme level.




I decided to combine the Lync client SDK with the speech synthesizer SDK and not only get myself a persistent popup notification, but have the new notification read to me, assuming my Lync status was not DND or Busy.


Step 1

Add these classes to your project.

using Microsoft.Lync.Model;
using Microsoft.Lync.Model.Conversation;
using System.Diagnostics;
using System.Speech;
using System.Speech.Synthesis;



Step 2

Declare some variables

LyncClient lyncClient;
List<ConversationHandler> conversations = new List<ConversationHandler>();
SpeechSynthesizer synth = new SpeechSynthesizer();

From here you need to assign some values and add some handlers somewhere like the window loaded event.

lyncClient = LyncClient.GetClient();

lyncClient.ConversationManager.ConversationAdded += ConversationManager_ConversationAdded;
lyncClient.ConversationManager.ConversationRemoved += ConversationManager_ConversationRemoved;


Now as a conversation gets added you can create a conversation handler for each one, that can then notify you when you receive a new message in that conversation

var conversationHandler = new ConversationHandler(conversation);
conversationHandler.MessageError += new MessageError(conversationService_MessageError);
conversationHandler.MessageRecived += new MessageReceived(conversationService_MessageRecived);
conversations.Add(conversationHandler);
//starts listening to Lync events
conversationHandler.Start();



The conversation handler cs code is attached here

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Lync.Model;
using Microsoft.Lync.Model.Conversation;


namespace LyncNotifier
{
/// <summary>
/// Called when a message is received from Lync.
/// </summary>
public delegate void MessageReceived(string message, string participantName, string nuri);

/// <summary>
/// Called when there was an issue with the conversation.
/// </summary>
public delegate void MessageError(Exception ex);
/// <summary>
/// Registers for conversation and participants events and responds to those by
/// notifying the UI through the events. This is the main point for interactions
/// with the Lync SDK.
/// </summary>

public class ConversationHandler
{
//the conversation the translator is associated with
private Conversation conversation;
public Conversation Conversation
{
get { return conversation; }
set { conversation = value; }
}

//Self participant's IM modality for sending messages
private InstantMessageModality myImModality;


/// <summary>
/// Occurs when a message is received from Lync.
/// </summary>

public event MessageReceived MessageRecived;

/// <summary>
/// Occurs when there was an issue with the conversation.
/// </summary>

public event MessageError MessageError;

/// <summary>
/// Receives the conversation, callback to UI and the OC root object
/// </summary>

public ConversationHandler(Conversation conversation)
{
//stores the conversation object
this.conversation = conversation;
//gets the IM modality from the self participant in the conversation
this.myImModality = (InstantMessageModality)conversation.SelfParticipant.Modalities[ModalityTypes.InstantMessage];
}

/// <summary>
/// Hooks into the conversation events so incoming messages are translated.
/// </summary>
public void Start()
{
//subscribes to new participants (in more people joins the conversation
conversation.ParticipantAdded += new EventHandler<ParticipantCollectionChangedEventArgs>(conversation_ParticipantAdded);
//registers for all existing remote participants messages
foreach (Participant participant in conversation.Participants)
{
//skips the self participant (only remote messages are translated)
//Note: Self participant also fires InstantMessageReceived
//In this case, we're not interested in watching messages
//sent by the self participant (actually reported as InstantMessageReceived)
if (participant.IsSelf == false)
{
SubcribeToParticipantMessages(participant);
}
}
}

/// <summary>
/// Called by the Lync SDK when a new participant is added to the conversation.
/// </summary>

private void conversation_ParticipantAdded(object sender, ParticipantCollectionChangedEventArgs args)
{
//subscribes to messages sent only by a remote participant.
if (args.Participant.IsSelf == false)
{
SubcribeToParticipantMessages(args.Participant);
}
}

/// <summary>
/// Register for InstantMessageReceived events for the specified participant.
/// </summary>
/// <param name="participant"></param>
private void SubcribeToParticipantMessages(Participant participant)
{
//registers for IM received messages
InstantMessageModality remoteImModality = (InstantMessageModality)participant.Modalities[ModalityTypes.InstantMessage];
remoteImModality.InstantMessageReceived += new EventHandler<MessageSentEventArgs>(remoteImModality_InstantMessageReceived);
}

// <summary>
/// Called by the Lync SDK when a new message is received.
/// </summary>
private void remoteImModality_InstantMessageReceived(object sender, MessageSentEventArgs args)
{

try
{
//casts the modality
InstantMessageModality modality = (InstantMessageModality)sender;
//gets the participant name
string name = (string)modality.Participant.Contact.GetContactInformation(ContactInformationType.DisplayName);

//reads the message in its plain text format (automatically converted)
string message = args.Text;
//List<string> mods = (List<string>)modality.Participant.Contact.GetContactInformation(ContactInformationType.InstantMessageAddresses);
string nuri = modality.Endpoint.Uri.ToString();
//string nuri = mods[0];

//notifies the UI about the new message
MessageRecived(message, name, nuri);
}
catch (Exception ex)
{
MessageError(ex);
}
}

// <summary>
/// Sends a notification about the composing state.
/// </summary>
public void SendComposingNotification(bool isComposing)
{
//send the event 'as-is' without checking the success
try
{
myImModality.BeginSetComposing(isComposing, null, null);
}
catch (LyncClientException e)
{
var eventHandler = MessageError;
if (eventHandler != null)
{
eventHandler(e);
}
}
catch (SystemException e)
{
//if (LyncModelExceptionHelper.IsLyncException(e))
//{
// // Handle the exception thrown by the Lync Model API.
// var eventHandler = MessageError;
// if (eventHandler != null)
// {
// eventHandler(e);
// }
//}
//else
//{
// Rethrow the SystemException which did not come from the Lync Model API.
throw;
//}
}
}
}
}

The final result will be a notification to your procedure conversationService_MessageReceived that will contain the message, participant name, and participant uri.

At this point you can do anything you want, I chose to speak the message if not busy;

synth.SpeakAsync(youmessage);

and then display a visual notification right about the task bar clock.




Sorry I can't give you more, but this is part of a commercial product, hopefully it's enough to get you started.




Doug Routledge, C# Lync, SQL, Exchange, UC Developer







1 comment:

  1. Thanks for a great code. Now it works kind of as i want it to.
    Thanks for all the help.. you made my evening here.. :D

    ReplyDelete