Lync MSPL “Busy Here” Script Project

*****Before I begin I just wanted to give a shout out to Melding Technology for helping with this project. They basically took an unfinished script that a reader found out on the internet and fixed it to a working state. So thanks to Jay and Angela for working on the script.*******

image

I got a question from a reader about a MSPL script he had found on the web from this TechNet forum post.

http://social.technet.microsoft.com/Forums/eu/ocsvoice/thread/f5829e31-42bd-4a65-bf1a-4777c5c54770

Basically the script didn’t run but was supposed to enable busy on busy. With some help from Melding Technology here in Seattle this script is now functional but with some limitations. Apparently the original script was close to working but needed some small changes. Currently it doesn’t check if a user is enabled for Unified Messaging and just returns a busy signal if already on a call. I will post updates if  they enable this. This script does however check if a user is defined for this feature by looking up the user.txt file. So as long as you define the user in User.txt they will get busy on busy if they are on a call when a second call arrives. Also beware this script has not been tested in a large scale roll out. It has only been tested in a lab setting so use at your own risk.

The links below are access to both the script and user file from my Public Skydrive share. Please feel free to grab and use this. I have posted the full text of the script below to view before downloading the file.

Script

https://skydrive.live.com/redir.aspx?cid=25a5ce54e91be979&resid=25A5CE54E91BE979!712&parid=25A5CE54E91BE979!142 

User text file

https://skydrive.live.com/redir.aspx?cid=25a5ce54e91be979&resid=25A5CE54E91BE979!711&parid=25A5CE54E91BE979!142

<?xml version="1.0" ?>
<lc:applicationManifest
lc:appUri="http://www.meldingtechnology.com/busybusy"
xmlns:lc="http://schemas.microsoft.com/lcs/2006/05">
  <lc:allowRegistrationBeforeUserServices action="true" />
  <lc:requestFilter methodNames="INVITE"
                          strictRoute="true"
                          registrarGenerated="true"
                          domainSupported="true" />
  <lc:responseFilter reasonCodes="NONE" />
  <lc:proxyByDefault action="true" />
  <lc:scriptOnly />
  <!--
    -
    - users.txt
    - Users database file. The BusyBusy Filter will only be applied to the users listed in
    - this file.
    -
    -->
  <lc:file name="usersTable" path="users.txt" delimitedBy="whitespace" keyColumnName="uri">
      <!-- Filter applies to this URI. -->
      <lc:column name="uri" />
  </lc:file>

  <lc:splScript>
    <![CDATA[
    // Function that returns true if the given content has the SDP audio m lines
    // and false otherwise
    function contentHasSDPAudio(content) {
        // SDP format is strict enough that the following check is a valid
        // way to determine if the offer includes audio.
        if (ContainsString(content,"\nm=audio ", false) ||
            ContainsString(content,"\rm=audio ", false)) {
            //Log( "Debug", false, "***BusyBusy***: found m=audio" );
            return true;
        }
        return false;
    }

    //Log ("Event", false, "***BusyBusy***: started script");
    Log( "Debug", false, "***BusyBusy***: We have a request - ", sipRequest.Method );

    // Check if this is a re-INVITE and exit if so
    foreach ( sessionExpires in GetHeaderValues( "Session-Expires" ) ) {
      if ( ContainsString( sessionExpires, "refresher", true ) ) {
          Log( "Debugr", false, "***BusyBusy***: skipped; This is a session refreshing invite" );
          return;
      }
    }

//
// Get user@domain string for the user in the To: header.
//
toUri = GetUri(sipRequest.To);
toUserAtHost = Concatenate( GetUserName( toUri ), "@", GetHostName( toUri ) );

Log( "Debugr", false, "***BusyBusy***: toUserAtHost         - ", toUserAtHost );

//
// Look up the user in our user table
//
user= usersTable[toUserAtHost].uri;

// Comment this if block if you want to apply the filter to everybody
if(user == null)
{
    Log("Debug", false, "***BusyBusy***: ignoring filter for this user." );
    return;
}


if (sipRequest.StandardMethod == StandardMethod.Invite) {
    // Determine if this request is for an audio session
    hasBody = false;
    hasAudio = false;
    foreach (header in GetHeaderValues (StandardHeader.ContentType)) {
        // Found a content-type header, so we know it has a body.
        hasBody = true;
        if (IndexOfString (header, "multipart/", true) == 0) {
            //Log( "Debugr", false, "***BusyBusy***: Found multipart body. Content-Type:", header );
            i = 0;
            while (i<MultiPartItem.Count && BindMultiPartBodyItem(i)) {
                if (ContainsString(MultiPartItem.ContentType, "application/sdp", true)) {
                    //Log("Debugr", false, "***BusyBusy***: Found SDP content-type in Multipart item count: ", i);
                    if (contentHasSDPAudio(MultiPartItem.Content)) {
                        //Log("Debug", false, "***BusyBusy***: content has audio" );
                        hasAudio = true;
                        break;
                    }
                }
                i=i+1;
            }
        }
    }

    if ( hasAudio ) {
        Log("Debug", false, "***BusyBusy***: this is an audio call" );
    }
    else if (!hasBody) {
        // An INVITE without a body is taken as an implied audio INVITE.
        Log("Debug", false, "***BusyBusy***: content has no body, implied to be an audio call" );
    }
    else {
       // not an audio call retuern now
       Log("Debug", false, "***BusyBusy***: this is not an audio call!" );
       return;
    }
}


totalEndpoints = 0;
anyEndpointBusy = false;

foreach (dbEndpoint in QueryEndpoints(toUserAtHost)) {
   totalEndpoints = totalEndpoints + 1;
  
   Log( "Debugr", false, "***BusyBusy***: endpoint.EPID        - ", dbEndpoint.EPID );
   Log( "Debugr", false, "***BusyBusy***: endpoint.ContactInfo - ", dbEndpoint.ContactInfo );
   //Log( "Debugr", false, "***BusyBusy***: endpoint.Instance    - ", dbEndpoint.Instance    );


   publication = QueryCategory(toUri, 2, "state", dbEndpoint.Instance);
   //Log( "Debugr", false, "***BusyBusy***: publication - ", publication );

   if (IndexOfString(publication, "on-the-phone") >= 0) {
      Log( "Debugr", false, "***BusyBusy***: endpoint in a call change to busy state" );
      anyEndpointBusy = true;
      break;
   }
   else {
      Log( "Debugr", false, "***BusyBusy***: endpoint not in call stay in free state" );
   }

}

//Log( "Debugr", false, "***BusyBusy***: found ", totalEndpoints, " endpoint(s)" );


// Respond busy if any endpoint was busy...
if (anyEndpointBusy) {
    if (RequestTarget.Aor != "BENOTIFY") {
       Respond( 486, "Busy here" );
       Log( "Debugr", false, "***BusyBusy***: Busy responce given for", toUserAtHost);
       //log a request which was replied with busy signal
       Log( "Event" , true,  "***BusyBusy***: Busy responce given for", toUserAtHost);
       }

}

Log( "Debugr", false, "***BusyBusy***: finished script");

return;
   
]]>
  </lc:splScript>
</lc:applicationManifest>

If you want to use this script and make changes please let me know. This is a community project that started as a post on a forum so please share if you make improvements, changes etc.

Comments welcomed.

VoIPNorm

Blocking Lync Audio and Video with Federated Contacts Using MSPL

I am a huge fan of MSPL scripting. I have published a few here on VoIPNorm and I have pointed numerous IT Pros to them. Certainly there is common theme to controlling Lync federated partners whether it be for file transfer or other with these scripts.

 

The following is a script that was written by Santosh More from Microsoft that blocks audio and video with federated contacts. The scripts I post aren't always going to be block buster scripts that everyone will use but they can be great starting points to accomplish other tasks if you new to MSPL.

 

Thanks to Santosh for allowing me to share this with everyone.

 

<?xml version="1.0"?>

<lc:applicationManifest lc:appUri="http://fabrikam.com/BlockFederatedAudioCalls" xmlns:lc="http://schemas.microsoft.com/lcs/2006/05">

<lc:requestFilter methodNames="INVITE" strictRoute="true"  phoneUris="true"/>

  <lc:responseFilter reasonCodes="ALL"/>

  <lc:scriptOnly/>

  <lc:proxyByDefault action="true"/>

<lc:splScript><![CDATA[

  

 

                if (sipRequest)

                {

       

                 if (!ContainsString(sipRequest.To, "conf:audio-video"))

                  {

 

                                if (ContainsString(sipRequest.From, "@contoso.com"))

                                {

       

                                  if (IndexOfString(sipRequest.Content, "m=audio") >= 0)

                                  {

                                  Respond(403, "AV call with federated domain is not allowed");

                                  }

    

                                }

               

                                if (ContainsString(sipRequest.To, "@contoso.com"))

                                {

       

                                  if (IndexOfString(sipRequest.Content, "m=audio") >= 0)

                                  {

                                  Respond(403, "AV call with federated domain is not allowed");

                                  }

    

                                }

 

                    }

 

                  }

               

]]>

 

</lc:splScript>

</lc:applicationManifest>

 

Following are the Steps to deploy the application

 

1. Modify the Script replace @contoso.com with the domain name of the Federated partner and save the file as BlockFederatedAudioCalls.am in following folder C:\LyncScripts\BlockFederatedAudioCalls  on all the Edge Servers.

 

2. On FrontEnd Server run following command to create a server application

 

New-CsServerApplication -Identity "EdgeServer:edgepool.fabrkam.com/BlockFederatedAudioCalls" -Uri "http://fabrikam.com/BlockFederatedAudioCalls" -ScriptName

"C:\LyncScripts\BlockFederatedAudioCalls\BlockFederatedAudioCalls.am" -Enabled $true

 

3. This change needs to be replicated to all the Edge Servers so wait for CMS  replication to complete with Edge servers

   Or Export the Configuration and Import it on the Edge servers.

  

4. Restart Access Edge Service on All Edge Servers

 

If you have a script that you want to share with the world let me know. I am happy to post it on VoIPNorm and give full credit to the creator.

 

VoIPNorm

Looking for the Perfect Lync App, Then Look No Further

Clive Graven from Microsoft has put together this great directory of Lync applications. No more searching. Check it out.

http://www.ucoffice.com/Pages/default.aspx

Comments welcomed.

VoIPNorm