Send an Instant Message from a Script

Here are a couple of interesting facts:

 

· Elephants can make sounds that humans can’t hear.
People who work closely with elephants know this because if you get close enough you can feel the sound waves.

· Earth, and more accurately life on Earth, would not exist without Jupiter.
Jupiter is so large its gravitational field pulls in most of the space junk – stray comets and asteroids and things – before they can move on and crash into Earth.

 

These are just a couple pieces of trivia this author has stumbled upon over the years and has managed to remember because she found them interesting. Has she used this information for anything useful in her life? (For that matter, has she used any information for anything useful in her life? We won’t try to answer that.) Has it helped her in her career at all? (For that matter, has anything… Well, we already know the answer to that.) The answer to both questions is no; this information has been completely useless to her.

 

Which brings us to the topic of this article. Here at the Lync Server PowerShell blog we like to think the articles and scripts we publish are actually useful to some of you. As a matter of fact, most of the time we write about things people have specifically asked us about. This article started out that way, but, like so many things in life, we ran into some snags and everything sort of veered off course. But on the chance that some of you might still find this useful, or at least interesting, we’ve decided to go ahead and show you what we came up with.

 

Note. Okay, we’re being a little harsh here. The scripts we’re about to show you can actually come in pretty handy, but, as we’re about to explain, you have to go through a few steps to get started.

 

We began with the intent of figuring out how to perform some client management tasks in Microsoft Lync Server. In this case, we wanted to figure out how to use a Windows PowerShell script to send an instant message (IM) to various Lync contacts or groups.

 

Now, a script that sends an instant message can potentially be pretty useful. Suppose you’re running a report, and you want to send an instant message to all your contacts in a particular group when the report has finished running. Wouldn’t it be nice to have a script that would do that for you? Or you have an announcement you’d like to quickly IM to all your contacts. That could be useful, too. Unfortunately, Lync Server 2010 doesn’t provide any cmdlets that can help you with this. So we turned to the Microsoft Lync 2010 SDK.

 

The good news is that we accomplished what we set out to. We have four scripts here for you. Each one sends an instant message from the currently logged on user to various sets of that user’s contacts. From the testing we did they all run flawlessly and can easily be modified to monitor processes and send the message based on process completion.

 

Now for the not-so-good news. (We never have “bad” news for our customers here at Microsoft. Remember: it’s a feature.) As we said, the way we found to do this was by using the Microsoft Lync 2010 SDK. SDKs are typically made to be used by application developers, not so much by script writers. So while you can use them in scripts, keep in mind that there are sometimes extra things you need to do to work with them. To start with, in order to use the Lync 2010 SDK you need to download and install it. But, before you can do that you need to download and install the Microsoft Silverlight 4 Tools for Visual Studio 2010. And before you do that, you need to make sure you have either Microsoft Visual Studio 2010 with the Visual Studio Web Developer feature or Microsoft Visual Web Developer 2010 Express installed. (The latter is a free download.) Like we said, the assumption is you’re using this in a development environment, so of course you’d be running Visual Studio, right?

 

Now, an interesting thing about all this is that in order to use the SDK with Windows PowerShell, all you really need are the DLLs that the SDK installs. That means that once you have all this installed, you can simply copy the DLL files (and, in the case of the scripts we’re going to show you here, you need only one DLL) and the scripts you want to run to the whatever computer you want to run from and you can run the scripts from there.

 

Yes, we know, that seems like a lot of trouble to go to for one little DLL. But that’s the way it goes.

 

Note. The DLL you’ll need is Microsoft.Lync.Model.DLL, saved by default in the folder C:\Program Files (x86)\Microsoft Lync\SDK\Assemblies\Desktop. If you copy the DLL to a computer rather than install the full SDK you’ll need to register the DLL before these scripts will work. To register the DLL, type this at a command prompt:

 

regsvr32 Microsoft.Lync.Model.DLL

 

If you don’t run the command from the folder where you put the DLL you’ll need to include the full path with the filename.

 

The final thing to note before we continue is that these scripts run against the currently running instance of Lync on the local machine. You must be logged on to Lync for the scripts to run. The instant messages that are sent will be sent from the user logged in to Lync. In addition, the scripts work against the contacts in that user’s instance of Lync. They don’t access all contacts within a Lync Server deployment.

 

Send an Instant Message to All Your Contacts

 

So now that we’ve explained what you have to do to get ready for this, we’ll show you the first script. (The script is going to seem like child’s play after all that.) This script will send an instant message to all contacts in the running instance of Microsoft Lync:

 

$assemblyPath = “C:\Program Files (x86)\Microsoft Lync\SDK\Assemblies\Desktop\Microsoft.Lync.Model.DLL”

Import-Module $assemblyPath

 

$added = @{}

$PlainText = 0

$IMType = 1

 

$cl = [Microsoft.Lync.Model.LyncClient]::GetClient()

 

$conv = $cl.ConversationManager.AddConversation()

 

$gs = $cl.ContactManager.Groups

 

foreach ($g in $gs)

{

 

    foreach ($contact in $g)

    {

               

        if (!$added.ContainsKey($contact.Uri))

        {

            $null= $conv.AddParticipant($contact)

            $added.Add($contact.Uri,0)

        }

           

    }

 

}

 

$d = New-Object "System.Collections.Generic.Dictionary[Microsoft.Lync.Model.Conversation.InstantMessageContentType,String]"

$d.Add($PlainText, "This is just a test.")

 

$m = $conv.Modalities[$IMType]

$null = $m.BeginSendMessage($d, $null, $d)

 

The first two lines of code are the same for every script we’re going to show you in this article:

 

$assemblyPath = “C:\Program Files (x86)\Microsoft Lync\SDK\Assemblies\Desktop\Microsoft.Lync.Model.DLL”

Import-Module $assemblyPath

 

The first line places the full path to the DLL named Microsoft.Lync.Model.DLL into a variable ($assemblyPath). The path shown here is the default path the DLL is installed to. If you didn’t install to the default path, or if you’ve copied the DLL to a different location, be sure to update this path. The second line calls the Import-Module cmdlet, passing it the variable with the path to the DLL. This makes it so we can use all the classes, methods, properties, and so on that are in that DLL.

 

Next we create some variables:

 

$added = @{}

$PlainText = 0

$IMType = 1

 

The $added variable is an array (that’s what the @{} means) that is currently empty. We’ll be using this array to store a hash table. If you’re not sure what a hash table is, don’t worry, we’ll get to that in a little bit. We’ll use the second variable, $PlainText, to specify what type of message we’ll be sending. A value of 0 represents a plain text message. We’ll use $IMType to tell Lync what type of conversation we’re going to be starting. The value 1 represents an instant messaging conversation.

 

Now it’s time to connect to our Lync client:

 

$cl = [Microsoft.Lync.Model.LyncClient]::GetClient()

 

Remember that you have to have Lync up and running and be logged on for this command to work. All this is doing is calling the GetClient method to get a reference to the client and store it in the variable $cl. This allows us to access Lync from our script.

 

In Lync, an instant message is a type of conversation. (Other types of conversations include conferences and phone calls.) Because we want to send an instant message, we need to start a conversation in our running instance of Lync. We do that by calling the AddConversation method, like this:

 

$conv = $cl.ConversationManager.AddConversation()

 

Now that we’ve started a conversation, we need to find the contacts who we want to participate in that conversation. In this script we’re going send the instant message to all of our contacts, so we need to grab every contact in every group in our contacts list. We start by getting all of our groups and storing them in the variable $gs, like this:

 

$gs = $cl.ContactManager.Groups

 

Now we need a foreach loop to loop through each group:

 

foreach ($g in $gs)

 

Next we get to do what’s called a “nested loop,” which is simply one loop within another. As we get each group, we need to loop through that group and pull out each contact. We do that with another foreach loop:

 

foreach ($contact in $g)

 

This is where things get a little tricky. It’s possible that a contact can be in more than one group. For example, you might have a contact in your Current Contacts group and your Frequent Contacts group. We want to add each contact as a participant in our IM conversation, but we can only add each contact once. If we try to add a contact more than once our script will return an error. So we need to make sure that we haven’t already added a contact to the conversation before we add them. There are probably many ways you can do this, but we decided to go with a hash table.

 

A hash table is basically just an array, but it stores values as key/value pairs. That means that each entry has a key, which must be unique, and an associated value. By adding a contact’s URI to the hash table when we add them to the conversation, the next time we go through the loop we can check the table to see if the contact is already there. If they are, we know not to try to add them to the conversation again. Here’s what that looks like:

 

        if (!$added.ContainsKey($contact.Uri))

        {

            $null= $conv.AddParticipant($contact)

            $added.Add($contact.Uri,0)

        }

 

We start by using the ContainsKey method to check our hash table (which, you’ll recall, is stored in $added). We pass ContainsKey the URI of the current contact. Notice the exclamation point (!) at the beginning. This means not. So what the if statement says is “if the $added hash table does not contain a key that matches the URI of the current $contact, then we want to go into the if statement and do what follows.”

 

The first thing that follows is a call to the AddParticipant method of the conversation we created. We pass AddParticipant the contact object, which adds that contact to our conversation.

 

Note. Notice that we’ve assigned the output of our call to AddParticipant to $null. AddParticipant displays some information to the screen about the contact that’s being added. We don’t want to see that information, so we just assign it to a null value and it goes away without us ever having to see it.

 

Now that we’ve added the contact as a participant in the conversation, we add the contact’s URI to the hash table so we won’t try to add them to the conversation again:

 

$added.Add($contact.Uri,0)

 

Note. Because a hash table needs a key and a value for every entry, we put in zero for the value. It doesn’t really matter what you put in as the value, since we don’t need it for this script. We’re just using the hash table for its ability to keep unique keys.

 

Once all our contacts have been added as participants to the conversation, it’s time to create the instant message.

 

Well, almost. Did we say the hash table was the tricky part? Well, that’s true. But in order to send an instant message, we need to create something very similar to a hash table: a Dictionary object. A Dictionary object, just like a hash table, contains key/value pairs. The big difference is that these pairs get stored in an object rather than as an array. We have to use the .NET Framework’s Dictionary object because that’s what Lync is going to be expecting when we tell it to send the message. We know, it’s a little complicated, but that’s how things sometimes go when you’re working with SDKs. So, let’s create our Dictionary object:

 

$d = New-Object "System.Collections.Generic.Dictionary[Microsoft.Lync.Model.Conversation.InstantMessageContentType,String]"

$d.Add($PlainText, "This is just a test.")

 

We call the New-Object cmdlet to create the object. We need to specify the entire class name for the object, System.Collections.Generic.Dictionary. (Why? Because that’s the way Windows PowerShell works with .NET objects.) That’s followed by a set of square brackets ([]) that contain the data types for the keys and values we’ll be storing in the Dictionary. In this case the key contains the type of content we’re sending in our message, while the value is a simple text string.

 

It’s possible to send messages other than text messages. Here’s a list of message types, and the values that you use as the key to specify that type:

 

Message Type

Value

Description

PlainText

0

Plain text format.

Html

1

Html format.

RichText

2

Rich text format.

Gif

3

Gif (Graphics Interchange Format) format.

Ink

4

Ink format.

Unknown

5

Format is unknown.

 

 

After we’ve created the Dictionary object, we call the Add method to add the text we want to send in our instant message:

 

$d.Add($PlainText, "This is just a test.")

 

We’re sending our instant message as plain text, so the first parameter – the key – is the variable $PlainText that we declared earlier and contains the value 0. The second parameter is the actual text of our instant message.

 

We’ve now created a conversation, added participants, and created the message we want to send. There’s one more step left before we can actually send the message. We need to tell Lync what type of modality to use in sending the message. In other words, what type of conversation is this? For Lync 2010 there aren’t a lot of choices:

 

Modality Type

Value

Description

None

0

No modalities

InstantMessage

1

Instant Messaging Modality

AudioVideo

2

Audio/Video Modality

Reserved1

4

Reserved for future use.

Reserved2

8

Reserved for future use.

 

We’re sending an instant message, so we’ll be using a value of 1 (which we stored earlier in the variable $IMType):

 

$m = $conv.Modalities[$IMType]

 

Now, it’s finally time to send our message. We do that by calling the BeginSendMessage method:

 

$null = $m.BeginSendMessage($d, $null, $d)

 

Once again we’ve assigned the output to $null so we don’t get any unnecessary text scrolling down our screen. We pass the BeginSendMessage method the Dictionary object ($d) that contains the instant message, a Null value (don’t worry about this, just always use $null when calling this method from PowerShell), and the Dictionary object again.

 

And that, finally, is that. Keep in mind that this script opens a conversation window on your desktop that you’ll probably want to close at some point.

 

 

Send an Instant Message to a Specific Group

 

This next script is a lot like the one we just looked at, but instead of sending the instant message to all your contacts, it sends the message only to the contacts in a specific group. Here’s the script:

 

$assemblyPath = “C:\Program Files (x86)\Microsoft Lync\SDK\Assemblies\Desktop\Microsoft.Lync.Model.DLL”

Import-Module $assemblyPath

 

$IMType = 1

$PlainText

 

$cl = [Microsoft.Lync.Model.LyncClient]::GetClient()

 

$conv = $cl.ConversationManager.AddConversation()

 

$gs = $cl.ContactManager.Groups

 

foreach ($g in $gs)

{

 

    if ($g.Name -eq "Most Used Contacts")

    {

 

        foreach ($contact in $g)

        {

               

            $null= $conv.AddParticipant($contact)

 

        }

 

    }

 

}

 

$d = New-Object "System.Collections.Generic.Dictionary[Microsoft.Lync.Model.Conversation.InstantMessageContentType,String]"

$d.Add($PlainText, "This is just a test.")

 

$m = $conv.Modalities[$IMType]

$null = $m.BeginSendMessage($d, $null, $d)

 

There are two differences between this script and the previous script. The first is that we don’t need to check for duplicate participants. In Lync 2010 a contact can exist only once in a group, and because we’re limiting our conversation participants to contacts in a single group that means that we’ll never run across the same contact twice. This simplifies the script a little since we no longer have to create a hash table and check for duplicates.

 

The second difference is this if statement:

 

if ($g.Name -eq "Most Used Contacts")

 

Here we’re checking the Name property of the group to see if it’s equal to (-eq) “Most Used Contacts”. This is what’s stored as the Name for the Frequent Contacts group. If the current group is the Most Used Contacts group, then we loop through that group and add the contacts as participants in the instant messaging session.

 

Note. Why isn’t the name of the Frequent Contacts group “Frequent Contacts”? Well, we’re not sure. We’re guessing it’s a legacy thing.

 

Send an Instant Message to a Single Contact

 

You might think that sending an instant message to one contact would be simpler than sending an instant message to multiple contacts at once. Well, that might be true when you’re working directly in the user interface, but in a script you have to search through your contacts to find that user before you can send the message. So the script is just a tiny bit longer than those we’ve looked at so far. Here it is:

 

$assemblyPath = “C:\Program Files (x86)\Microsoft Lync\SDK\Assemblies\Desktop\Microsoft.Lync.Model.DLL”

Import-Module $assemblyPath

 

$LastName = 38

$IMType = 1

$PlainText = 0

 

$cl = [Microsoft.Lync.Model.LyncClient]::GetClient()

 

$conv = $cl.ConversationManager.AddConversation()

 

$gs = $cl.ContactManager.Groups

 

$i = 0

 

foreach ($g in $gs)

{

    foreach ($contact in $g)

    {

        if ($contact.GetContactInformation($LastName) -eq "Myer")

        {

            $i++

       

            $null= $conv.AddParticipant($contact)

           

            break

        }

    }

 

    if ($i -gt 0) {break}

}

 

$d = New-Object "System.Collections.Generic.Dictionary[Microsoft.Lync.Model.Conversation.InstantMessageContentType,String]"

$d.Add($PlainText, "Still testing")

 

$m = $conv.Modalities[$IMType]

$null = $m.BeginSendMessage($d, $null, $d)

 

This script begins like the others: We import the DLL, declare some variables, grab hold of the Lync client, start a conversation, then begin looping through the contacts in each group. Then we run into this:

 

        if ($contact.GetContactInformation($LastName) -eq "Myer")

        {

            $i++

       

            $null= $conv.AddParticipant($contact)

           

            break

        }

    }

 

    if ($i -gt 0) {break}

 

In this example we want to send the instant message to Ken Myer. So for each contact we find we call the GetContactInformation method, passing it the variable $LastName, which will retrieve the last name of the contact. We check to see if that name is equal to Myer. If it is, we add that contact as a participant to our conversation. This obviously isn’t foolproof: it’s possible to have more than one contact with the last name Myer. You might want to check for a contact based on the contact’s URI or maybe the first and last names. If you do have more than one contact with the last name Myer this script will find the first one and then stop looking. That’s what all the break statements are for. Within the if statement the first thing we do is increment the variable $i. (The syntax $i++ means “add 1 to the value of $i.”) After we add the contact we see the break statement. The break statement will throw us out of the foreach loop that is looping through the contacts in the current group. We found the contact we want, so we don’t need to keep looking. However, we’re still in the foreach loop that’s looping through all the groups. That’s what the second if statement is for:

 

if ($i -gt 0) {break}

 

Remember how we added 1 to the value of $i (which we had earlier initialized to 0) when we found the contact we were looking for? Well, here we’re simply saying that if $i is greater than (-gt) 0 then we can break out of the foreach loop that’s looping through the groups.

 

Once we’ve added our single participant and broken out of the foreach loops the rest of the script is the same as the others: We create the instant message, set the message type, and send it.

 

 

Send an Instant Message to Multiple Contacts

 

This script is kind of a combination of the first script (send an IM to all contacts) and the previous (send an IM to a single contact). This one sends an instant message to two specific contacts: Ken Myer and Pilar Ackerman:

 

$assemblyPath = “C:\Program Files (x86)\Microsoft Lync\SDK\Assemblies\Desktop\Microsoft.Lync.Model.DLL”

Import-Module $assemblyPath

 

$added = @{}

$LastName = 38

$IMType = 1

$PlainText = 0

 

$cl = [Microsoft.Lync.Model.LyncClient]::GetClient()

 

$conv = $cl.ConversationManager.AddConversation()

 

$gs = $cl.ContactManager.Groups

 

foreach ($g in $gs)

{

    foreach ($contact in $g)

    {

        if ($contact.GetContactInformation($LastName) -eq "Myer" –or `

            $contact.GetContactInformation($LastName) -eq "Ackerman")

        {

            if (!$added.ContainsKey($contact.Uri))

            {

                $null= $conv.AddParticipant($contact)

                $added.Add($contact.Uri,0)

            }

        }

    }

}

 

$d = New-Object "System.Collections.Generic.Dictionary[Microsoft.Lync.Model.Conversation.InstantMessageContentType,String]"

$d.Add($PlainText, "This is just a test.")

 

$m = $conv.Modalities[$IMType]

$null = $m.BeginSendMessage($d, $null, $d)

 

There actually isn’t anything in this script we haven’t seen so far. We’ve simply added an –or statement to check for two contacts:

 

        if ($contact.GetContactInformation($LastName) -eq "Myer" –or `

            $contact.GetContactInformation($LastName) -eq "Ackerman")

 

We again use the hash table (stored in the $added variable) to ensure that we don’t try to add a contact to the conversation more than once.

 

 

Tell Me Again Why We Did This

 

And that’s about it. Although working with .NET and SDKs in Windows PowerShell has its challenges, the scripts themselves are really pretty short and simple.

 

Will you ever use these scripts? Well, we don’t know. But like we said, we figured out how to send an instant message from within a script, so we thought we’d let you know what we came up with. It’s up to you to decide how or if you have a need for these or anything like them. If you have any questions or want to know anything else about working with the Lync 2010 SDK in Windows PowerShell, just let us know.

Comments

  • Anonymous
    January 01, 2003
    Hey Ramesh, Thanks for the feedback, and for the correction! You're right of course, there are two SDKs, one for Lync and one for Lync Server, and this article works only with the Lync SDK. We've made the correction. Thanks again!

  • Anonymous
    January 01, 2003
    Hey Ivan, Thanks for the feedback! We knew someone would find a use for this. :-) The Lync SDK has a lot of stuff in it, there's definitely more you can do. We'll continue playing around with it and see what else we can come up with.

  • Anonymous
    May 06, 2011
    This is great information!  We have to fully test every function of Lync after any upgrade, Windows Patch, or scheduled reboot of any part of the system.  It is a very manual process now.  This may help us fill some of the holes in the Synthetic Transactions that we need for full product testing. Are there more features we can test with the Lync SDK and PowerShell? Thank you!

  • Anonymous
    May 09, 2011
    Nice article, hopefully more developers find it useful in the field. Just one correction - You have mentioned "Lync Server 2010 SDK" instead of "Lync 2010 SDK" in the article. If you could correct it, that would be great as the Lync Server 2010 SDK is a different SDK and meant for server side programming. Thanks, Ramesh