Using the Skype Web SDK from any language or framework


Saturday, 12 December 2015

Share with: 
 

Back in June, I wrote a article on how to quickly and easily send an Instant Message (IM) using the Skype Web SDK.

The examples showed the use of simple JavaScript and a HTML wrapper for entering credentials, the participant (destination) and the message itself.

I followed this up in August with an even simpler solution. Since then, people have asked how to send Instant Messages from other languages, including PHP and Java.

The Skype Web SDK is a JavaScript library, so it's not that easy to call from another language. However, we can built a simple mini-API that you can call using a HTTP POST from any other language.

Overview

The code below demonstrates how to build a hosted API, with just one function - send an Instant Message - using the Skype Web SDK. Technically it's not really an API, for the purposes of describing it, it's the easiest term.

The API is a HTML and JavaScript file that must be hosted on a site with two prerequisites:

  • Internet Connectivity to the Skype Web SDK CDN
  • Connectivity to your Lync or Skype for Business Environment
Example of the IM flow for this application

Prerequisites

The code below can only be run on a web server and Lync or Skype for Business environment that has been configured to support UCWA, and thus the Skype Web SDK.

See my Skype Web SDK Prerequisites article for more information on how to configure the SDK.

Step 3 - Cross Domain Authorization is the most important - you must add the domain name where you're hosting this code to your Lync or Skype for Business server.

The API

Our code is just a single html page, that accepts a number of querystring or HTTP post values.

It's intended to be called remotely, either using a HTTP GET or POST, and in the examples below we'll use a local webserver - IIS Express that comes with Visual Studio. It can be hosted on any web server (not just IIS), but remember to take into account the security considerations discussed at the end of this article.

Variables

Variable Purpose
recipient A Lync or Skype for Business recipient, typically their SIP/Email address. In this example, matthew@contoso.com will receive the instant message.
message The actual Instant Message content, URL encoded. In the example below, the message is "hello world". In the example, '%20' is the URL encoding equivalent of a space.
username OPTIONAL - The API starts with a hardcoded username and password, however you can override this if you wish you send an IM from a different user account. The username is the SIP/Email address of the sender's Lync or Skype for Business account. Leave this and the password field blank if you wish to use the credentials stored in the JavaScript file.
password OPTIONAL - If you specify a username above, you also need to specify the password for this account.

API Usage Example - Stored Credentials


http://localhost/index.html?recipient=matthew@contoso.com&message=hello%20world
	

API Usage Example - Defined Credentials


http://localhost/index.html?recipient=matthew@contoso.com&message=hello%20world&username=matthew@contoso.com&password=password123
	

The example above demonstrates passing user credentials as part of the querystring - not really a very safe idea - but useful for demonstration purposes. If you really must do this, check out the security consideration comments below.

Application Flow

The API, written entirely in JavaScript, is remarkably simple and linear. Aside from error checking, it logs in, starts a chat service, sends the message, stops the service and logs out.

In a little more detail, here's how it works.

  1. Initialize the Skype Web SDK
  2. Check for the required variables, recipient and message
    • If either are missing, the application stops
  3. Check for any optional variables, username and password
  4. Sign in using the signInManager.SignIn(username/password) method
    • If successful, start the chatService using the .start() method
      • If successful, add a participant/recipient
        • If successful, send the message using the chatService.sendMessage() method!
  5. Stop the chat service using the .stop() method
  6. Sign out using the signInManager.signOut() method
  7. That's it!

Download

You can download the code to this project from GitHub, or check out the code below.

Download Follow @matthewproctor

The API Code

I've based this code on a previous example as mentioned before. Additional error checking has been added, as well as code to check for the existance of the 4 variables shown above.


<!doctype html>
<html>
<head>
    <!-- SkypeWeb library requires IE compatible mode turned off -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- the jQuery library written by John Resig (MIT license) -->
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.3.min.js"></script>
    <!-- SkypeWebSDK Bootstrap Libray -->
    <script src="https://swx.cdn.skype.com/shared/v/1.1.23.0/SkypeBootstrap.min.js"></script>
    <!-- Javascript used in this demonstration -->
    <script src="index.js"></script>
</head>
<body>
<div id="logging_box" contenteditable="false" class="code"></div>
</body>
</html>


// replace this with the credentials of the Lync or Skype for Business account
// that will be used to send the Instant Message.  Change these to your own, as 
// they are not real!
var skype_username = "matthew@contoso.com";
var skype_password = "mypassword";

// this is the recipient to whom we'll send a message - retrieved from the querystring or HTTP POST data
var recipient = "";
// and this is the actual message  - retrieved from the querystring or HTTP POST data
var the_message = "";

// show_logs determines whether logs are sent to the logging_box div. Set to true
// to display logging.  Disabling logs improves performance somewhat.
var show_logs = true;


// function to extend jQuery to easily check for and return a querystring value
function getParameterByName(name) {
	name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
	var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
		results = regex.exec(location.search);
	return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}	
	
function pause(howlongfor){
	log("Pausing for " + howlongfor + "ms");
	var currentTime = new Date().getTime();
	while (currentTime + howlongfor >= new Date().getTime()) {      }
}

function nicetime() {
	var d = new Date();
	return padLeft(d.getHours(), 2) + ":" + padLeft(d.getMinutes(), 2) + ":" + padLeft(d.getSeconds(), 2) + ":" + padLeft(d.getMilliseconds(), 3);
}

function log(texttolog) {    
	if (show_logs){
		$('#logging_box').append(nicetime() + ": " + texttolog + "<br>");
	}
}
function padLeft(nr, n, str) { return Array(n - String(nr).length + 1).join(str || '0') + nr; }

$(function () {
	'use strict';

	var Application
	var client;
	var conversation;

	Skype.initialize({
		apiKey: 'SWX-BUILD-SDK',
	}, function (api) {
		Application = api.application;
		client = new Application();
		log("Client Created");
		
		// Check querystring for required variables - recipient and message

		if (getParameterByName('recipient')!=""){
			recipient = getParameterByName('recipient');
			log("Found Recipient: " + recipient);
		} else {
			log("No recipient found!");
			return;
		}
		
		if (getParameterByName('message')!=""){
			the_message = getParameterByName('message');
			log("Found message: " + the_message);
		} else {
			log("No message found!");
			return;
		}
		
		// Now check for optional variables - username and password
		if (getParameterByName('username')!=""){
			skype_username = getParameterByName('username');
			log("Found skype username: " + skype_username);
		} 
		if (getParameterByName('password')!=""){
			skype_password = getParameterByName('password');
			log("Found password.");
		} 

		log('Signing in ' + skype_username);
		client.signInManager.signIn({
			username: skype_username,skype_password
		}).then(function () {
			log('Logged In Successfully');
		  
			//create a new conversation
			log("Creating a new Conversation");
			conversation = client.conversationsManager.createConversation();

			log("Starting chatService");
			conversation.chatService.start().then(function () {
				log('chatService started!');

				conversation.addParticipant("sip:" + recipient).then(function () {
					log(recipient + " added!");

					pause(1000);
					log('Sending message: ' + the_message);
					conversation.chatService.sendMessage(the_message).then(function () {
						log('Message sent.');

						pause(1000);

						conversation.chatService.stop().then(function () {
							log('chatService stopped.');
						}).then(null, function (error) {
							log('Error Stopping chatService:' + error);
						});

						log("Signing Out");
						client.signInManager.signOut().then(
							function () {
								log('Signed out');
							},
						function (error) {
							log('Error signing out:' + error);
						});

					}).then(null, function (error) {
						log('Error Sending Message:' + error);
					});                   
					

				}).then(null, function (error) {
					log('Error adding participant:' + error);
				});

			}).then(null, function (error) {
				log('Error starting chatService' + error);
			});                       
			
		}).then(null, function (error) {
			// if either of the operations above fails, tell the user about the problem
			log("Error signing in: "+error );
		});

	}, function (err) {
		log('some error occurred: ' + err);
	});

});

 

Using the API

The API is simply called via a HTTP GET or POST call, from your favourite language or framework, passing it the variables required.

Before you can use it, there are three variables to configure in the JavaScript file:


var skype_username = "matthew@contoso.com";
var skype_password = "mypassword";
var show_logs = true;
    
  • skype_username - Replace with the Lync or Skype for Business account username
  • skype_password - Replace with your the account password
  • show_logs - Set to true to output logs to the HTML page, or false to suppress them.

If you don't want to hardcode a username and password in the JavaScript file, you can leave these blank (or put anything you want in them), and pass the credentials in the HTTP GET or POST as variables.

 

Example code in VB, C#, Java, jQuery, PHP, Python and Ruby

Below are some basic examples of how to call the API from a variety of other languages and frameworks. I'm not an expert in these, so there may be more elegant or appropriate ways of calling the API, but this should get you started! :)

 

VB.NET


Imports System
Imports System.IO
Imports System.Net
Imports System.Text

Public Shared function postIMMessage(byval senderGUID as string, byval recipientGUID as string, byval IMMessage as string) as string
    ' Create a request using a URL that can receive a post. 
    Dim request As WebRequest = WebRequest.Create("https://localhost/index.html?recipient=" & recipientGUID & "&message=" & IMMessage)
    ' Set the Method property of the request to POST.
    request.Method = "POST"
    ' Get the response.
    Dim response As WebResponse = request.GetResponse()
    ' Display the status.
    Console.WriteLine(CType(response, HttpWebResponse).StatusDescription)
    ' Get the stream containing content returned by the server.
    dataStream = response.GetResponseStream()
    ' Open the stream using a StreamReader for easy access.
    Dim reader As New StreamReader(dataStream)
    ' Read the content.
    Dim responseFromServer As String = reader.ReadToEnd()
    ' Clean up the streams.
    reader.Close()
    dataStream.Close()
    response.Close()
    ' Returns the response 
    return responseFromServer
End Function
	

 

C#


Using System;
Using System.IO;
Using System.Net;
Using System.Text;
                                        
public static string postIMMessage(string senderGUID, string recipientGUID, string IMMessage)
{
	// Create a request using a URL that can receive a post. 
	WebRequest request = WebRequest.Create("https://localhost/index.html?recipient=" + recipientGUID + "&message=" + IMMessage);
	// Set the Method property of the request to POST.
	request.Method = "POST";
	// Get the response.
	WebResponse response = request.GetResponse();
	// Display the status.
	Console.WriteLine(((HttpWebResponse)response).StatusDescription);
	// Get the stream containing content returned by the server.
	dataStream = response.GetResponseStream();
	// Open the stream using a StreamReader for easy access.
	StreamReader reader = new StreamReader(dataStream);
	// Read the content.
	string responseFromServer = reader.ReadToEnd();
	// Clean up the streams.
	reader.Close();
	dataStream.Close();
	response.Close();
	// Returns the response 
	return responseFromServer;
}
	

 

Java


string urlParameters = "{ 'username': 'matthew@contoso.com', 'password': 'password123', 'recipient': 'anotherperson@contoso.com', 'message': 'Hello World!' }";
String request = "https://localhost/index.html";
URL url = new URL(request); 
HttpURLConnection connection = (HttpURLConnection) url.openConnection();           
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setInstanceFollowRedirects(false); 
connection.setRequestMethod("POST"); 
connection.setRequestProperty("Content-Type", "application/json"); 
connection.setRequestProperty("charset", "utf-8");
connection.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters.getBytes().length));
connection.setUseCaches (false);

DataOutputStream wr = new DataOutputStream(connection.getOutputStream ());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
connection.disconnect();

	

 

jQuery


$.ajax({
    beforeSend: function(xhrObj){
        xhrObj.setRequestHeader('Content-Type','application/json');
        xhrObj.setRequestHeader('Accept','application/json');
    }
    type: 'POST',
    url: 'https://localhost/index.html',       
    data: '{ "username": "matthew@contoso.com", "password": "password123", "recipient": "anotherperson@contoso.com", "message": "Hello World!" }',               
    dataType: 'json',
    success: function(json){
    }
});

 

PHP


$postData = array(
    'username' => 'matthew@contoso.com',
    'password' => 'password123',
    'message' => 'Hello World!'
);
                    
$context = stream_context_create(array(
    'http' => array(
        // https://localhost/index.html
        'method' => 'POST',
        'header' => "Content-Type: application/json\r\n",
        'content' => json_encode($postData)
    )
));

$response = file_get_contents('https://localhost/index.html', FALSE, $context);

if($response === FALSE){
    die('An error has occurred');
}

$responseData = json_decode($response, TRUE);	

 

Python


import urllib
params = urllib.urlencode({ 'username': 'matthew@contoso.com', 'password': 'password123', 'recipient': 'anotherperson@contoso.com', 'message': 'Hello World!' })
f = urllib.urlopen("https://localhost/index.html", params)
print f.read()	

 

Ruby


uri = URI('https://localhost/index.html')
req = Net::HTTP::Post.new(uri, initheader = {'Content-Type' =>'application/json'})
req.body = {username: 'matthew@contoso.com', password: 'password123', 'recipient': 'anotherperson@contoso.com', 'message': 'Hello World!'}.to_json
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(req)
end
    	

Security Considerations

As you can see from the code above, credentials are being stored in the JavaScript fine. This is typically really, really bad™, and is shown here for example and testing purposes. If you must store credentials this way, then I recommend the following precautions be taken.

  • DO NOT host the API on an internet-facing server - keep it internal, behind a firewall, at the minimum.
  • The user account used to send IM's should not have ANY domain priviledges, aside from being Lync or Skype for Business user with rights to send an IM. No login, no group membership, nothing, nil, null.
  • The credentials shown in this article and the supporting code examples are not real. Don't try them, they won't work. :)

This code is provided without any warranty or guarantees, and is shown as an example only. If you use it, and you don't follow good security principals, then it's at your own risk.

Tags

Skype, Skype Web SDK, Lync, UCWA
Let's build a wrapper around the Skype Web SDK to allow any language or framework to quickly and easily send an Instant Message.
 
 

Popular Articles

What is Kutamo?
Kilimanjaro 2015
Exploring Lync and IoT
Exchange 2013 in 60 minutes
Monitoring Lync with MRTG
Lync UCWA Tutorial - Introduction
Tutorial Parts 1 | 2 | 3 | 4 | 5

Recent Articles

Australian Postal Codes
Skype Web SDK
Using the Skype Web SDK from any language or framework
Building a Skype for Business Auto Responder using the Skype Web SDK
Exporting Lync or Skype for Business Contacts with the Skype Web SDK

Favourite Links

Kutamo
Telco Together Foundation
Cloud on Kilimanjaro

Tags / Keywords

Skype, Skype Web SDK, Lync, UCWA