Skype sender as a web service

I used the fantastic Python module Skpy to setup a ‘Skype sender’ as a web service. I did this work because I wanted to make it as easy to programmatically post to a Skype channel as it is to programmatically post to a Slack channel. If you know your channel id and have an API key, then posting a message is as trivial as executing a curl command. All internal applications at Qxf2 can now simply post to a HTTPS endpoint to send Skype messages. This is especially useful when sending out weekly reminders, integrating with CI/CD tools, etc.

Note: I am not proud of the code quality and I think it’s quality could be improved greatly. But given how useful it has been, I figured it was worth sharing anyway.


Doesn’t Skype already have a REST API?

Yes, Skype does expose an API. In fact, the Python module we use in this post (skpy) wraps around the REST API provided by Skype. The problem is that Skype seems to have several checks for suspicious logins and usually forces new logins to use the browser to fill out a captcha. Which in turn means that posting messages programmatically is a flaky experience. To work around this, I created a web service that our internal applications can call to post messages. The web service has one Skype account (called Qxf2Bot) logged in perpetually from one machine and we use that account to post messages to our common channel. This particular implementation has been going strong for a while now without any hiccups.


Implementation details

I chose to write the web application in Flask since I needed nothing more than a micro-framework for something so simple. There are three parts to the web application worth showing snippets for:
1. The Skype login
2. Sending a message to Skype using skpy
3. The endpoint to send a message

For those in a hurry, you can find the entire code on this GitHub gist for Skype Sender.

1. The Skype login
There is one Skype object logged in perpetually using a token file. The token file needs to be renewed every time it expires but skpy makes it easy to do that. For the login part, I used the following code:

def login(username, password, token_file='.tokens-app'):
    "Login to Skype"
    sk = Skype(connect=False)
    sk.conn.setTokenFile(token_file)
    try:
        sk.conn.readToken()
    except SkypeAuthException:
        sk.conn.setUserPwd(username, password)
        sk.conn.getSkypeToken()
 
    return sk

Note: If you had been using skpy successfully and then hit the Couldn’t retrieve t field from login response error, try using the above code snippet to perform the login.

2. Sending a message to Skype using skpy
The code snippet to send a message to Skype looks like this:

def post_message(sk, channel_id, msg):
    "Post a message to a given channel"
    result_flag = False
    try:
        sk.conn.verifyToken(SkypeConnection.Auth.SkypeToken)
        channel = sk.chats.chat(channel_id)
        channel.sendMsg(msg, rich=True)
        result_flag = True
    except Exception as e:
        print(e)
 
    return result_flag

Note that the sk.conn.verifyToken method uses the current token if it is valid. But it will automatically renew the token if the token has expired.

3. The endpoint to send a message
The application has just one useful endpoint /send-message. Users can POST to this endpoint by supplying an API key, message, and channel. The code for this endpoint looks like this:

@app.route('/send-message', methods=['POST'])
def send_message_endpoint():
    "Send the required message"
    request_data = request.get_json()
    api_key = request_data.get('API_KEY', '')
    channel_id = request_data.get('channel', None)
    msg = request_data.get('msg', None)
    if api_key != creds.API_KEY:
        return jsonify({'msg':'incorrect API key'}), 401
    if not msg or not channel_id:
        return jsonify({'msg':'missing request parameters'}), 400
    sk = login(creds.USERNAME, creds.PASSWORD)
    post_message(sk, channel_id, msg)
 
    return jsonify({'msg':'Posted message'}), 200

Usage

I hosted this application as a subdomain of qxf2.com. Now, our users can simply use their favourite language and make a POST to the /send-message endpoint. An example of using curl against this endpoint would look like this:

curl -X POST -d '{"msg":"Example for the blog post", "channel":"the_skype_channel_id", "API_KEY":"This is a secr3t!"}' -H 'Content-Type: application/json' https://skype-sender.qxf2.com/send-message

This interface maps nicely with how Slack lets applications post messages programmatically.


Next few posts

This post is the first part of me documenting the Skype bot we use at Qxf2. There will be several follow up post in the coming months. One will show how we setup a Skype listener that will allow us to do some cool magic in the future. I hope to also share how we created an AWS lambda to call this endpoint, thereby making our Skype sender available to our internal applications that are hosted on our AWS infrastructure as well. And finally, when we are ready internally, I would love to share how we go about testing such a loosely coupled application that consists of micro-services, an AWS pipeline, and has a tiny bit of intelligence to it.


Leave a Reply

Your email address will not be published. Required fields are marked *