Monday, November 8, 2010

Thunderbird new mail notification for freedesktop compliant systems

For a while it bothered me, that Thunderbird doesn't not provide an integrated new mail notification for freedesktop compliant systems - it doesn't display new mail icon in system tray. While there is moztraybiff add-on - it's not up to date for a while already, and it requires recompilation for each particular OS.

Recently I came across Mailbox Alert add-on which allows executing a custom program when new mail appears in a given folder (it can be conveniently configured for each folder separately). This gave me an idea how to make a simple new mail notification alert. So I wrote a small script for that (it uses wmctrl utility which appeared not to be installed by default on my OpenIndiana and Debian Linux where I tested this script, but it's available for installation from standard repositories).

Create let's say ~/bin/tb_new_mail_alert.sh as follows:
#!/bin/bash

# Author: Shmerl
# Date  : November, 2010

. ~/bin/helpers/locking.sh

TB_WINDOW_SUBTITLE='Mozilla Thunderbird'
NEW_MAIL_ALERT_ICON='/usr/share/icons/gnome/48x48/status/mail-unread.png'

exclusive_lock_require `basename $0` 1 2>&1 > /dev/null
zenity --notification --window-icon="$NEW_MAIL_ALERT_ICON" --text='New mail has arrived!'

# Currently it just raises some matching window.
# This can be modified to address specific cases like
# two Thunderbirds running in parallel using different profiles etc.
# In that case the script can take profile name as a parameter and
# use that to alter the lock name and window subtitle pick.

if [ `wmctrl -l | grep -c "${TB_WINDOW_SUBTITLE}"` -ne 0 ]
then
   wmctrl -a "$TB_WINDOW_SUBTITLE" # bring to front
fi

Note: On Debian I'm using it with actual Thunderbird, but if you want to use it with Icedove you should modify TB_WINDOW_SUBTITLE value above accordingly.

Here is locking.sh (included as ~/bin/helpers/locking.sh) which is needed to ensure that only one instance of the notification script will be able to run (thanks to Jason Weathered from Stack Overflow for this example).

#!/bin/bash

#
# The code is taken from Stack Overflow
# Thanks to Jason Weathered
#

function exclusive_lock_try() # [lockname]
{
   local LOCK_NAME="${1:-`basename $0`}"

   LOCK_DIR="/tmp/.${LOCK_NAME}.lock"
   local LOCK_PID_FILE="${LOCK_DIR}/${LOCK_NAME}.pid"

   if [ -e "$LOCK_DIR" ]
   then
      local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
      if [ ! -z "$LOCK_PID" ] && ps -p "$LOCK_PID" &> /dev/null
      then
         # locked by non-dead process
         echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
         return 1
      else
         # orphaned lock, take it over
         ( echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null && 
         local LOCK_PID="$$"
      fi
   fi
   if [ "`trap -p EXIT`" != "" ]
   then
      # already have an EXIT trap
      echo "Cannot get lock, already have an EXIT trap"
      return 1
   fi
   if [ "$LOCK_PID" != "$$" ] &&
      ! ( umask 077 && mkdir "$LOCK_DIR" && umask 177 &&
         echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null
   then
      local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
      # unable to acquire lock, new process got in first
      echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
      return 1
   fi
   trap "/bin/rm -rf \"$LOCK_DIR\"; exit;" EXIT

   return 0 # got lock
}

function exclusive_lock_retry() # [lockname] [retries] [delay]
{
   local LOCK_NAME="$1"
   local MAX_TRIES="${2:-5}"
   local DELAY="${3:-2}"

   local TRIES=0
   local LOCK_RETVAL

   while [ "$TRIES" -lt "$MAX_TRIES" ]
   do
      if [ "$TRIES" -gt 0 ]
      then
         sleep "$DELAY"
      fi
      local TRIES=$(( $TRIES + 1 ))

      if [ "$TRIES" -lt "$MAX_TRIES" ]
      then
         exclusive_lock_try "$LOCK_NAME" > /dev/null
      else
         exclusive_lock_try "$LOCK_NAME"
      fi
      LOCK_RETVAL="${PIPESTATUS[0]}"

      if [ "$LOCK_RETVAL" -eq 0 ]
      then
         return 0
      fi
   done

   return "$LOCK_RETVAL"
}

function exclusive_lock_require() # [lockname] [retries] [delay]
{
   if ! exclusive_lock_retry "$@"
   then
      exit 1
   fi
}


Make sure you point zenity to the existing icon (I used Gnome's mail-unread.png in the example above. After you set up these scripts and install Mailbox Alert add-on, right click on the mail folder in Thunderbird for which you want to receive notifications, and select "Mailbox Alert". Enable "Execute Command" field there and point it to the tb_new_mail_alert.sh and you should be set.

A useful thing to add would be an indication of current number of new messages since Mailbox Alert allows to pass it to the script. The downside so far - the notification doesn't go away, until the icon is actually clicked. If add-on would provide some action hook for "no more new mail" kind of event - this could be improved.

No comments:

Post a Comment