philgroce.com

Prototype ergo propter type

Send jabber.el Alerts Through Growl

Let’s say you’re using Emacs as your Jabber client. (Stop looking at me like that.) You like it well enough, but you’d like it to be a little more aggressive about telling you when you have a new message.

Oh, and you’re using a Mac. Because Macs have Growl. (You might be able to make this work with GNotify or KNotify on Linux/*BSD/etc. or Snarl on Windows.)

Making Emacs send notifications of all kinds through Growl (including those from jabber.el) turns out to be surprisingly easy. Here’s how I did it.

Growl

First you need a way for Emacs to talk to Growl. This is easy, thanks to growlnotify. In fact, it’s already been written at least twice.

I’m kind of partial to that last one, but I’ve tweaked it a bit, thusly:


(defvar growl-program "/usr/local/bin/growlnotify")

(defun growl (title message &optional id)
  (if (eq id nil)
      (start-process "growl" " growl"
                     growl-program title "-w")
    (start-process "growl" " growl"
                   growl-program title "-w" "-d" id))
  (process-send-string " growl" message)
  (process-send-string " growl" "\n")
  (process-send-eof " growl"))



All this does is call growl “title” -w and echo the message into it. (growlnotify seems to prefer taking its messages from STDIN.) The -w keeps the subprocess around until the notification is dismissed—at least long enough to pipe the message into it. I found that if I didn’t, the process would go away before I could pipe in the message.

If passed an id argument, growl calls growlnotify with -d <id>. This keeps the desktop clean; if a notification balloon is already active with that id, Growl will reuse it instead of making another one. This is a particular problem with group chats; the server keeps a log of recent messages and sends them to you when you join. Without -d, this results in Growl notification spam taking up a quarter or more of your screen real estate.

jabber.el

To tell jabber.el to notify us via Growl when people talk to us, add a hook to jabber-alert-message-hooks (for one-on-one conversations) and another to jabber-alert-muc-hooks (for groupchats or chatrooms, a.k.a multi-user chats, or MUCs).


;; Make jabber.el notify through growl when I get a new message
(setq jabber-message-alert-same-buffer nil)
(defun pg-jabber-growl-notify (from buf text proposed-alert)
  "(jabber.el hook) Notify of new Jabber chat messages via Growl"
  (when (or jabber-message-alert-same-buffer
            (not (memq (selected-window) (get-buffer-window-list buf))))
    (if (jabber-muc-sender-p from)
        (growl (format "(PM) %s" 
                       (jabber-jid-displayname (jabber-jid-user from))) 
               (format "%s: %s" (jabber-jid-resource from) text)
               (format "jabber-from-%s" (jabber-jid-resource from)))
      (growl (format "%s" (jabber-jid-displayname from)) 
             text "jabber-from-unknown"))))
(add-hook 'jabber-alert-message-hooks 'pg-jabber-growl-notify)

;; Same as above, for groupchats
(defun pg-jabber-muc-growl-notify (nick group buf text proposed-alert)
  "(jabber.el hook) Notify of new Jabber MUC messages via Growl"
  (when (or jabber-message-alert-same-buffer
	    (not (memq (selected-window) (get-buffer-window-list buf))))
    (if nick
        (when (or jabber-muc-alert-self
                  (not (string= 
                        nick (cdr (assoc group *jabber-active-groupchats*)))))
          (growl (format "%s" (jabber-jid-displayname group))
                 (format "%s: %s" nick text)
                 (format "jabber-chat-%s" (jabber-jid-displayname group))))
      (growl (format "%s" (jabber-jid-displayname group)) 
             text "jabber-chat-unknown"))))
(add-hook 'jabber-alert-muc-hooks 'pg-jabber-muc-growl-notify)




Some of the niceties I’ve implemented above:

Conclusion and future work

This approach obviously has a lot of potential. It’s easy enough to configure new mail notification this way, but I tend not to like that sort of thing; IM on work-related topics is all the distraction I need. I do, however, plan to make this work with upcoming appointments in my org-mode agenda and/or diary. We’ll see how that goes.

Copyright © 2006-2008 Phil Groce. All Rights Reserved.