22 March, 2010

Общение со Skype через D-Bus на Python

Summary: в данной заметке описывается работа с программой Skype через D-Bus на Python.

Введение

Захотелось мне странного - когда я ухожу домой, мне нужно выключить amarok, kopete и Skype. Собственно, решено было через D-Bus отправлять вышеперечисленным приложениям релевантные сообщения.

Используем dbus-send

Сначала я использовал обычный dbus-send, что оформилось в виде следующего скрипта go2home:

#!/bin/sh

# Stop amarok
dbus-send --session --type=method_call --dest=org.kde.amarok /Player org.freedesktop.MediaPlayer.Stop

# Logout from kopete
dbus-send --session --type=method_call --dest=org.kde.kopete /Kopete org.kde.Kopete.disconnectAll 

# Logout from Skype
skypeapi.py 'SET USERSTATUS OFFLINE'

# Lock screen
dbus-send --session --type=method_call --dest=org.freedesktop.ScreenSaver /ScreenSaver org.freedesktop.ScreenSaver.Lock
Детали и параметры работы команды dbus-send описаны в man-странице

Проблема со скайпом

Со скайпом пришлось немного повозиться, так как для работы с ним необходима постоянная сессия, что нельзя сделать с помощью dbus-send.

Прочитав описание протокола на сайте

был создан нижеследующий скрипт: skypeapi.py

#!/usr/bin/env python
import dbus, sys

def main():
    remote_bus = dbus.SessionBus()
    
    # Check if skype is running.
    system_service_list = remote_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus').ListNames()
    skype_api_found = 0
    for service in system_service_list:
        if service=='com.Skype.API':
            skype_api_found = 1
            break
    if not skype_api_found:
        sys.exit('No running API-capable Skype found')

    # Get skype dbus api
    skype_service = remote_bus.get_object('com.Skype.API', '/com/Skype')

    # Connect to skype.
    answer = skype_service.Invoke('NAME SkypeApiClient')
    if answer != 'OK':
        sys.exit('Could not bind to Skype client')

    # Check if protocol is supported.
    answer = skype_service.Invoke('PROTOCOL 1')
    if answer != 'PROTOCOL 1':
        sys.exit('This test program only supports Skype API protocol version 1')

    # Invoke operations
    for arg in sys.argv:
        skype_service.Invoke(arg)
    
    return 0    

if __name__ == "__main__":
    main()

При первом запуске скрипта появится скайповский диалог с вопросом, можно ли разрешить приложению доступ к скайпу. После нажатия на "Да" Skype добавит наш скрипт в разрешённые и мы сможем управлять скайпом.

После этого для скрипта go2home был создана иконка на панели.

Заключение

Как видно, работа с DBus из Python проста и элегантна. В качестве домашнего упражнения предлагаю написать скрипт, который после разблокирования экрана будет запускать все нужные приложения.

21 March, 2010

Разработка макроса для TiddlyWiki

Summary: Пример разработки плагина для TiddlyWiki

Вступление

TiddlyWiki - это вики-движок, полностью написанный на JavaScript и хранящийся в одном файле (как сам движок, так и содержимое). Создатели позиционируют его как "переиспользуемую нелинейную персональную веб записную книжку".

Я давно использую TiddlyWiki в различных целях:

  1. По прямому назначению.
  2. Как систему GTD.
  3. Как домашнюю страницу на компьютере.

Моя домашняя страница на компьютере - это, если говорить терминами TiddlyWiki, тиддлер, содержащий ссылки на страницы, которые я часто посещаю.

Для удобства использования я разработал пару стилей CSS, так что ссылки были крупными.

Исходный код плагина

После длительного использования tiddlywiki в качестве домашней страницы я понял, что мне хочется, чтобы с каждой ссылкой была иконка сайта. Вручную вставлять картинки мне не хотелось, поэтому было решено написать собственный плагин.

/*{{{*/
version.extensions.faviconLinkMacro = {major: 0, minor: 1, revision: 0, date: new Date(2010,3,21)};
// Author: Oleg Atamanenko
config.macros.faviconLink = {}
config.macros.faviconLink.handler = function(place, macroName,  params, wikifier, paramString) {
  var linkBox = createTiddlyElement(place, "span", null, "favIcon", "");

  var args = paramString.parseParams("list",null,true);
  var link = getParam(args, "link", 'false');
  
  if (link != 'false'){
    urlParts = link.split('/');
    imgLink = urlParts[0] + "//" + urlParts[2] + "/favicon.ico";

    var imgElement = createTiddlyElement(linkBox, "img", null, "faviconImage", "");
    imgElement.src = imgLink;
    imgElement.width = 16;
    imgElement.height = 16;

    var linkTitle = getParam(args, "title", 'false');

    if(linkTitle == 'false'){
      linkTitle = link;
    }
    var linkElement = createTiddlyElement(linkBox, "a", null, "faviconLink", linkTitle);
    linkElement.href = link;
    linkElement.target = '_blank';
  }
}

/*}}}*/

Установка плагина

  1. Создаём новый тиддлер, называем его faviconLinkMacro
  2. Помечаем тиддлер тегом systemConfig, тогда TiddlyWiki при открытии страницы его подгрузит.

Использование плагина

  1. Плагин создаёт новый макрос, faviconLink, с двумя параметрами link и title.
  2. Вызов <<faviconLink link:'https://mail.google.com/mail' title:'mail'>> создаёт ссылку с картинкой с GMail

Настройка визульных стилей плагина

Макрос создаёт следующие классы:

  1. span.favIcon
  2. img.faviconImage
  3. a.faviconLink

Пример стилей

.links span {
  display: block;
  position: relative;
  padding: 0.7em;
  margin: 0.3em;
  min-width: 6em;
  float:left;
  text-align: center;
  font-weight: bold;
  font-size: 1.5em;
  font-family: "Gill Sans MT", "Candara", "Arial"; 
  border: 2px solid [[ColorPalette::SecondaryLight]];
  background-color: [[ColorPalette::SecondaryPale]];
}

.links span:hover {
  border: 2px solid [[ColorPalette::SecondaryMid]];
  background-color: [[ColorPalette::SecondaryLight]];
  color: [[ColorPalette::PrimaryMid]];
}

.links img.faviconImage {
  position: relative;
  padding-right: 10px;
}

Данный CSS необходимо добавить в тиддлер с именем StyleSheet

Пример тиддлера со ссылками

{{links{
<<faviconLink link:'http://delicious.com/dark.schakal/2read' title:'2read'>> <<faviconLink link:'https://mail.google.com/mail' title:'mail'>> <<faviconLink link:'http://www.google.com/reader/view' title:'rss'>> <<faviconLink link:'http://atamanenko.blogspot.com/' title:'blog'>> <<faviconLink link:'http://feedburner.google.com/fb/a/myfeeds' title:'feedburner'>> <<faviconLink link:'https://www.google.com/analytics/settings/' title:'analytics'>> <<faviconLink link:'http://wave.google.com' title:'wave'>> <<faviconLink link:'http://www.blogger.com/home' title:'blogger'>> <<faviconLink link:'http://twitter.com/' title:'twitter'>> <<faviconLink link:'https://www.dropbox.com/home#/' title:'dropbox'>> <<faviconLink link:'http://translate.google.com/toolkit/' title:'translator'>> <<faviconLink link:'http://sibir.megafon.ru/sendsms/' title:'SendSMS'>>
}}}
Вышеприведённый код тиддлера создаёт следующую страницу: