Artisan Numérique

/bureau/bureautique/evolution/dbus/ Ajouter de jolies notifications à Evolution

Ce qui fait la beauté de l'environnement GNU/Linux et plus particulièrement de ses bureaux, c'est sa plasticité, c'est à dire sa capacité à être adapté à vos besoins avec peu d'effort. En effet, plutôt que de chercher le soft qui répond à nos vibrantes espérances, il arrive souvent qu'une 30aine de lignes de code fasse un travail admirable. Cas d'usage pour ce billet, ajouter à Evolution la notification des nouveaux courriels avec nom de l'expéditeur et sujet, à travers la librairie libnotify déjà utilisé par exemple par Pidgin.

Réception des signaux via D-Bus

D-Bus est un bus de communication inter-applicatif qui, comme beaucoup d'autres outils du monde du libre, ne brille pas par sa documentation et sa profusion d'exemples simples. D'un point de vue pragmatique, il permet à une application de se rendre pilotable par une autre application (appel de méthode), mais aussi de déclencher des événements que d'autres applications peuvent attendre pour effectuer une opération.

Pour revenir à Evolution, ce dernier dispose dans la liste de ses greffons d'un spécifiquement conçu pour envoyer un signal D-Bus lorsqu'un nouveau courriel est arrivé. Un petit exemple aidant à comprendre, le plus simple est d'ouvrir une console et de lancer la commande dbus-monitor:

gastondbus-monitor
signal sender=org.freedesktop.DBus -> dest=:1.733 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
string ":1.733"
method call sender=:1.733 -> dest=org.freedesktop.DBus serial=3 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='method_call'"
method call sender=:1.733 -> dest=org.freedesktop.DBus serial=4 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='method_return'"
method call sender=:1.733 -> dest=org.freedesktop.DBus serial=5 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
string "type='error'"

signal sender=:1.405 -> dest=(null destination) serial=11849 path=/im/pidgin/purple/PurpleObject; interface=im.pidgin.purple.PurpleInterface; member=DrawingBuddy
int32 5711
signal sender=:1.405 -> dest=(null destination) serial=11850 path=/im/pidgin/purple/PurpleObject; interface=im.pidgin.purple.PurpleInterface; member=IrcSendingText
int32 6375
string "PING 1302184324
"
signal sender=:1.658 -> dest=(null destination) serial=4708 path=/org/gnome/evolution/mail/newmail; interface=org.gnome.evolution.mail.dbus.Signal; member=Newmail
string "imap://yoran@imap.tagazok.net/INBOX"
string "INBOX"
uint32 1
string "msg_uid:20861"
string "msg_sender:Yoran Brault <yoran.brault@arnumeral.fr>"
string "msg_subject:Essai de notification via DBUS"
Espionner les conversations D-Bus

Comme vous le voyez, ça défile assez vite. Surtout si des applications comme Pidgin, utilisables via D-Bus, sont lancées. Tous les blocs qui commencent par "signal" sont des événements envoyés par une application, qu'il est possible de "monitorer" pour effectuer des actions. Le dernier bloc dans la trace qui précède nous intéresse plus particulièrement car il indique qu'un nouveau message est arrivé :

signal sender=:1.658 -> dest=(null destination) serial=4708 path=/org/gnome/evolution/mail/newmail; interface=org.gnome.evolution.mail.dbus.Signal; member=Newmail
string "imap://gaston@imap.tagazok.net/INBOX"
string "INBOX"
uint32 1
string "msg_uid:20861"
string "msg_sender:Gaston <gastont@tagazok.net>"
string "msg_subject:Essai de notification via DBUS"
signal D-Bus d'arrivé d'un message

Nous avons là toutes les informations utiles : le sujet du courriel et l'adresse de l'expéditeur. Pour éviter de recevoir trop de données, nous pouvons limiter un peu les signaux reçus par dbus-monitor en lui spécifiant une sorte de filtre :

gastondbus-monitor "type='signal',interface='org.gnome.evolution.mail.dbus.Signal',member='Newmail'"
signal sender=:1.658 -> dest=(null destination) serial=4708 path=/org/gnome/evolution/mail/newmail; interface=org.gnome.evolution.mail.dbus.Signal; member=Newmail
string "imap://gaston@imap.tagazok.net/INBOX"
string "INBOX"
uint32 1
string "msg_uid:20861"
string "msg_sender:Gaston <gastont@tagazok.net>"
string "msg_subject:Nouvel essai de notification via DBUS"
filtrage des seuls nouveaux courriers

Voilà qui est mieux. Dans ce filtre nous nous limitons aux seuls "signaux", de type "org.gnome.evolution.mail.dbus.Signal" et de membre "NewMail". Il ne nous reste maintenant plus qu'à analyser ce bazar pour renvoyer la balle à libnotify.

Maintenant que nous savons détecter les messages entrants, nous allons passer à la partie notification via Libnotify.

Libnotify

Libnotify est une librairie permettant d'envoyer (via D-Bus) des notifications à un démon qui les affiche en sur-impression sur l'écran (OSD) en haut à droite. Vous pouvez tester cela très facilement en ligne de commande :

gastonnotify-send Tagazok Pouet
Envoi d'une notification

Le résultat sera une notification visuelle avec le premier paramètre comme titre et le second comme corps. Il est aussi possible d'ajouter une icône en utilisant le paramètre -i chemin/vers/icone.png.

Nous avons là la théorie de base, voyons comme faire fonctionner tout cela ensemble.

Rédaction du script de notification

Dans la première version de cette article, j'utilisais directement les commandes dbus-monitor et notify-send à travers un script PHP. Finalement j'en suis revenu car l'interprétation de la sortie de la commande dbus-moinitor est pour le moins hasardeuse ce qui me faisait louper pas mal de notification.

Comme je n'ai toujours pas la tonsure, j'ai préféré tenter ma chance avec Python qui a le mérite d'intégrer la gestion de D-Bus et de Libnotify en standard, sans avoir à faire appel à des commandes externes. Et tout cela en beaucoup moins de lignes que ma version initiale en PHP.

#!/usr/bin/python
# -*- coding: utf-8 -*-

import dbus, gobject, pynotify;
from dbus.mainloop.glib import DBusGMainLoop
from email.header import decode_header

# La fonction exécutée à l'arrivée d'un message
def on_evolution_new_mail(path, folder, foo, uid, sender, subject):
# Décodage des deux paramètres sender et subject
sender=decode_header(sender[sender.find(":")+1:])[0][0];
subject=decode_header(subject[subject.find(":")+1:])[0][0];

# On rend plus jolie le sender si besoin est
pos = sender.find("<");
if pos !=-1 :
sender = sender[0:pos]

# Envoi de la notification
n = pynotify.Notification(sender, subject)
n.show()


# Obtention du bus de la session
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()

# Abonnement au signal Evolution/Nouveau Message
bus.add_signal_receiver(on_evolution_new_mail, dbus_interface="org.gnome.evolution.mail.dbus.Signal", signal_name="Newmail")

# Boucle d'attente des messages
loop = gobject.MainLoop()
loop.run()
Script de notification evolution-dbus-libnotify

Conclusion

Cette approche d'utilisation de D-Bus facilitée par sont intégration dans python permet bien d'autres personnalisation de bureau. Un exemple, Pidgin ne dispose pas d'une notification propre des nouveaux messages lorsqu'il s'agit du protocole IRC. Qu'à cela ne tienne, un coup de dbus-monitor