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:
gaston$dbus-monitorsignal sender=org.freedesktop.DBus -> dest=:1.733 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquiredstring ":1.733"method call sender=:1.733 -> dest=org.freedesktop.DBus serial=3 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatchstring "type='method_call'"method call sender=:1.733 -> dest=org.freedesktop.DBus serial=4 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatchstring "type='method_return'"method call sender=:1.733 -> dest=org.freedesktop.DBus serial=5 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatchstring "type='error'"signal sender=:1.405 -> dest=(null destination) serial=11849 path=/im/pidgin/purple/PurpleObject; interface=im.pidgin.purple.PurpleInterface; member=DrawingBuddyint32 5711signal sender=:1.405 -> dest=(null destination) serial=11850 path=/im/pidgin/purple/PurpleObject; interface=im.pidgin.purple.PurpleInterface; member=IrcSendingTextint32 6375string "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=Newmailstring "imap://yoran@imap.tagazok.net/INBOX"string "INBOX"uint32 1string "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=Newmailstring "imap://gaston@imap.tagazok.net/INBOX"string "INBOX"uint32 1string "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 :
gaston$dbus-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=Newmailstring "imap://gaston@imap.tagazok.net/INBOX"string "INBOX"uint32 1string "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 :
gaston$notify-send Tagazok PouetEnvoi 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