Categories
Computers Programming Python

Down and Dirty Mail Notification

Following is a simple new mail notification implementation for the awesome window manager that leverages procmail. It’s main virtue is simplicity: there are about 20 lines of python code, 1 procmail recipe and several lines of code required in the rc file for awesome. The result is a numeric count of new email displayed in the statusbar.

First off, the general idea was to create my own new mail notifier. I wanted it to be simple. Since I just go and check all my email all at once, I didn’t see the immediate need for checking files and the like. I realized that my procmail setup afforded me a basically foolproof hook for know when email arrived, so I decided to start there. Since awesome supports dbus, all I required was a procmail recipe that ran a script to tell awesome to bump a count and display it.

As it turned out, the dbus portion proved to be the biggest thorn in my side. The problem is that the environment procmail runs in does not innately provide the information required to access the dbus. I mistakenly thought the problem was related to a UID issue and what user procmail actually runs as. This was incorrect- procmail does in fact execute as the user whose mail is being delivered. It turns out that there is an environment variable called DBUS_SESSION_BUS_ADDRESS that is required for access to a given user’s dbus. Without it, the call to connect to the session bus fails.

So, with that, here is the python code to connect to the dbus and communicate with awesome:

import dbus
import sys
import os
import re
import subprocess as sp

dsba_re = re.compile('DBUS_SESSION_BUS_ADDRESS=(.*?)\x00')
pgrep_cmd = ['pgrep', '-u', 'user', 'awesome']

DBUS_NAME = 'org.naquadah.awesome.awful'
DBUS_PATH = '/'
DBUS_INTERFACE = DBUS_NAME + '.Remote'

def dbus_send(cmd):
    bus = dbus.SessionBus()
    awesome = bus.get_object(DBUS_NAME, DBUS_PATH)
    awesome_iface = dbus.Interface(awesome, dbus_interface = DBUS_INTERFACE)
    awesome_iface.Eval(cmd)

def chkEnviron():
    if not os.environ.has_key('DBUS_SESSION_BUS_ADDRESS'):
        pid = sp.Popen(pgrep_cmd, stdout=sp.PIPE).communicate()[0]
        f = open(os.path.join('/proc', pid.rstrip(), 'environ'), 'r')
        env = f.read()
        f.close()
        m = dsba_re.search(env)
        os.environ['DBUS_SESSION_BUS_ADDRESS'] = m.group(1)

header = sys.stdin.readlines()
chkEnviron()
dbus_send('mailNotify("")')

The key bit is the chkEnviron function which guarantees the setup of the DBUS_SESSION_BUS_ADDRESS environment variable. From that point, the code is pretty straight forward. In truth, I would prefer a more pythonic means of getting the process ID, but there doesn’t seem to be anything like a pgrep available in the python libraries. Or, at least, I couldn’t find anything.

Of course, the mailNotify function needs to be implemented in the users awesome rc.lua. Here’s my function:

function mailNotify(text)
    if text ~= nil then
        mailcount = mailcount + 1
    else
        mailcount = 0
    end
    mymailnotify.text = "<span size=\"xx-large\" weight=\"bold\"> "..mailcount.." </span>"
    end

I know, it’s scandalously simple. The only even remotely interesting thing is the use of pango markup. Hardly revolutionary.

OH! We need a widget too…

mailnotify = widget({ type = "textbox" })

OUCH! I think I sprained a pinky typing that…

Last piece- the procmail recipe:

:0hc
| $HOME/bin/mailnotify

Still awake?

I have procmail manage spam processing with bogofilter, making my insertion point for this recipe right before the recipe that checks for messages marked unsure. Obviously, if I had more exotic needs, I might have to sprinkle it around, or place it elsewhere. But I don’t, so I didn’t.

This is all well and good, but there is one part missing. How to reset the mail count once mail has been read? Well, given the complete lack of sophistication involved, there really aren’t a lot of options. My approach of choice was to add some code to my rc.lua that checks for when my mail client is terminated. The most straightforward means to do this with awesome is using the unmanage_client signal. As it happens, I was already using that signal so I added a simple check for my mail client and simply call mailNotify() with no arguments.

The result is basically satisfying and effective. There is a noteworthy shortcoming from my perspective: the notification has no knowledge of whether I’ve checked my mail remotely. Not a show stopper, but one of those details we programmers hate to let go. I’ll be noodling it for awhile.

In the meantime, now I know when I’ve got mail.

5 replies on “Down and Dirty Mail Notification”

That’s a nice piece of pun-ditry…

FWIW- the coding is actually pretty basic. Often times it’s the “Why?” that’s more interesting. Like in this case- all that extra stuff in the python code to insure the proper environment variables were set. That took me longer to piece together than all the coding and debug combined!

Leave a Reply

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