Friends are like snow, they disappear when you pee on them.

External Content not shown. Further Information.

The general state of instant messaging is horrible. Google - once the most preferrable commercial XMPP provider - dropped federation, and some people say that they even plan to shut down the XMPP access at all. Facebook already announced that it will do so, but at least provides an open API as alternative, for which there is already an experimental BitlBee extension. Skype dropped its public API a while ago. So while the Linux folks keep building their new ivory tower, all the achievements of the last decades seem to slowly vanish.

Anyway, so there we are, at a time when one must be glad if the "non-computer-people" regularily use Facebook instead of Skype chats. But some do, and since I hate the original Skype client, I spent a few hours in search for alternatives to the public API. Even though I did not yet write anything really usable, I'd like to share what I found out, to give other people inspiration, and as notes for myself. Everything mentioned here is experimental. Furthermore, I am in no way affiliated with Skype or Microsoft.

In theory, it should be possible to access Skype chats via MSN. This page has a nice summary about MSN servers that work. However, though I can manage to log in, my Skype contacts are not shown, and I do not get Skype messages. Not sure what I am doing wrong.

Skype chats can now be accessed via Outlook's Webinterface. It would have been nice to use this webinterface with edbrowse, but it does not even load. It is probably possible to reverse engineer this interface. But then again, web interfaces can be changed, and obfuscated. However, this webinterface seems to use MSNP24, and people are trying to reverse engineer it. This is probably the most promising approach. On the other hand, it is probably violating some terms of use.

My approach was different: I was trying to use the official client, as one would have done with the old public API. I was trying to use AT-SPI, specifically pyatspi2, since orca appears to work well with Skype. I used the accerciser tool to examine Skype.

Skype uses 32 bit Qt, so be sure to have qt-at-spi for that architecture. Under x64 Ubuntu, the package is called qt-at-spi:i386. Make sure that QT_ACCESSIBILITY=1 for your session. If accerciser shows you an entry for Skype, you know that you did it correctly.

The following script works fairly reliable for me and shows the contacts in my contact list. I am using the client version 4.3.0.37. Furthermore, this only works with the English language setting, and the window should not be iconified.

# -*- coding: utf-8 -*-
import pyatspi
import time

reg = pyatspi.Registry
skype = None

def getElementBy(parent, prop):
    for i in range(0, parent.getChildCount()):
        if prop(parent[i]):
            return parent[i]
        else:
            child = getElementBy(parent[i], prop)
            if child != None:
                return child
                return None

for i in range(0, reg.getDesktopCount()):
    d = reg.getDesktop(i)
    for app in d:
        if app.name == "skype":
            skype = app

if skype is None:
    print("skype is None")
    exit (-1)

# the main window is unique (hopefully)
mainwindowmenu = getElementBy (skype, lambda x: x.get_role_name() == 'menu bar')
if mainwindowmenu is None:
    print("mainwindowmenu is None")
    exit(-1)

contacts = getElementBy(mainwindowmenu.get_parent(),
                        lambda x: x.name =='Contacts')
if contacts is None:
    print("contacts is None")
    exit(-1)

for i in range(0, contacts.getChildCount(), 7):
    if i != 0:
        p = contacts[i].get_position(0)
        print(contacts[i].name + " → (" + str(p.x) + ", " + str(p.y) + ")")

The less reliable part now is actually sending messages. If the contact list is in the foreground, and sufficiently large to show all contacts, then you can send double clicks on the contact you want to chat with, to open the chat window

p = contacts[14].get_position(0)
reg.generateMouseEvent(p.x+2, p.y+2, pyatspi.MOUSE_B3D)

As I said, this is not really reliable, it is very fragile. Maybe on an own dedicated X-Server with a huge virtual screen, this could be used. To actually set the message that is to be sent, I had to hack a bit: Skype sometimes keeps other message text boxes in the background. However, their size is always zero. The following is very bad style, as it creates a busy infinite loop if something goes wrong. But as a proof-of-concept, it is ok, I guess.

msg = None
while msg is None:
    msg = getElementBy(skype,
                       lambda x: (x.name == 'Send a message') and
                       (x.get_size().x != 0))
    time.sleep(.2)

msg.set_text_contents("This is a test")

So far so good. Sometimes Skype crashes when doing this. But mostly it works. It brings the text window to front. To actually send the message, we can send the Return key:

reg.generateKeyboardEvent(36, None, pyatspi.KEY_PRESSRELEASE)

Another way of sending keystrokes and even resizing and moving windows is xdotool(1), which could come handy when using this method to send messages.

To receive messages, it is probably the easiest to just poll the sqlite database main.db in ~/.Skype/, which is in a subdirectory named after the account name. It has a table "Messages". To get the timestamp of the last message (which you probably want when starting your script)

select MAX(timestamp) from Messages;

then, assuming it was 1421498680, messages can be queried using

select id, author, body_xml from Messages where timestamp >= 1421498680;

In summary, try to convince your friends to use Facebook if they really want a commercial chat provider. Run a good XMPP server (with auto-ping and low tcp keepalive timeout and XEP-0313 or even XEP-0136 I'll probably do so when jessie gets stable), and offer them accounts. And if you really have to use Skype, face the evil!

Update: This project seems nice.