Keeping a Lync Conversation Alive
The Microsoft Lync client will automatically terminate instant message conversations if there has been no activity for 15 minutes. The conversation window doesn’t close, but the conversation is punctuated with a message telling you that the conversation has been stored in history. If you then type another message it’ll still be sent, but under the hood, it’s being sent on a new conversation, with a different ConversationID.
This can be a problem if the conversation you’re having is with a UCMA bot. If the conversation has timed out before you reply then the bot may think that your reply is actually the start of a new conversation (as the ConversationID will be different) and respond unexpectedly.
In this scenario, we need a way to keep the conversation alive – to trick the client into not terminating the conversation.
Aside: before you break the rules you should what they are, and why they’re important. The Lync client terminating inactive conversations is a Good Thing, which will reduce server load and maintain performance. You should think long and hard about deciding to override it like this. If you do, then you will be responsible for terminating the conversation if it’s appropriate to do so before the client closes their chat window. In an ideal world, UCMA bots would keep their own conversation tracking state, and would therefore be able to match up a new conversation with an existing, inactive, one by SIP address. However, that’s not always possible or practical in all cases, which is why I’m suggesting this approach here.
If you search around on the internet you’ll find a method for keeping the conversation alive that involves sending a SIP INFO message to the client using this format:
imCall.BeginSendMessage(Microsoft.Rtc.Signaling.MessageType.Info, contentType, messageBody, headers, OnMessageSent, imCall);
I had some issues with this. Mostly, BeginSendMessage is a protected method, so I can’t just call it on a new, accepted, conversations. I need to look into this more, and work out why it’s not working for me when it does for everyone else 🙁 Any feedback gratefully received!
The initial idea was to periodically send the “user is typing” INFO message back to the client, to trick it into staying alive, as documented here. However, the problem with this is that these INFO messages are seemingly not evaluated by the Lync client when determining active state. It doesn’t matter how many of these you send, nor how often, the conversation still times out after 15 minutes.
I then toyed with the idea of sending either blank strings as messages (doesn’t work, you get an error), or HTML non-breaking spaces (does work, but creates an unsightly line break in the message each time).
Eventually, I came up with this. It’s not great, but it works. To steal from Scott Hanselman:
Disclaimer: I haven’t tested this. It might not work. It might cause all sorts of unintended off consequences in your log files, your performance, or your bowel movements. Use it entirely at your own risk, and don’t come to me if your Lync environment melts.
The trick is to send the client a SIP INFO message cancelling a file transfer request that doesn’t exist. The Lync client displays information about file transfers in the conversation window, so it “needs to know” about file transfers completing, failing or cancelling. I send a random ID as the transaction reference, one that I certainly didn’t create or initiate a file transfer with. The Lync client ignores this (there really isn’t much it can do with it), but the act of sending it does seem to do a good job of keeping the conversation window active:
System.Net.Mime.ContentType contentType = new System.Net.Mime.ContentType(“text/x-msmsgsinvite”);
contentType.CharSet = "UTF-8"; StringBuilder sb = new StringBuilder(); sb.Append("Application-Name: File Transfer\r\n"); sb.Append("Invitation-Command: CANCEL\r\n"); sb.Append("Invitation-Cookie: 12345\r\n"); string s = sb.ToString(); byte htmlBytes = Encoding.UTF8.GetBytes(s); Flow.BeginSendInstantMessage(contentType, htmlBytes, EndSendInstantMessage, Flow);
Not for Mobiles
It turns out, upon implementation testing, that this approach doesn't work with mobile clients. Specifically, on the iPhone client (which I'm guessing doesn't support file transfer), the message send is ignored and the EndSendInstantMessage will time out. I'm fairly sure that you can't use this to keep the phone client open [aside: actually, it seems that the phone client is really good at keeping the conversation open anyway]. I haven't yet tested this, and I haven't tried it on Android or Windows Phone clients yet, but I can imagine a similar problem occurring.