Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Emacs Wanderlust mail notifier
;;; -*- Mode: EMACS-LISP -*-
;;; A fancy notify hook for Wanderlust.
;;;
;;; Time-stamp: <2019-09-16 18:29:28 fred>
;;;
;; Note: elmo must be loaded before byte-compiling this. Otherwise,
;; when the hook runs it will produce an invalid-funcion error because
;; of the elmo-imap4-response-value macro.
;;;; Usage:
;;;
;;; 1. Set the variable wl-biff-check-folder-list to the list of
;;; folders you want biff to check.
;;;
;;; 2. Set the wl-biff-check-interval and wl-biff-use-idle-timer
;;; variables as you like them.
;;;
;;; 3. Add this to the wl-biff-new-mail-hook:
;;; (add-hook 'wl-biff-new-mail-hook 'fmg-biff-hook)
;;;
;;; If you have a gmail folder, you should probably set
;;; wl-strict-diff-folders to the list of gmail folders you want
;;; checked:
;;;
;;; (setq wl-strict-diff-folders
;;; '("%INBOX:username/clear@imap.gmail.com:993!"))
;;;
;;; The folder names in this list should match the entries in the
;;; folders file exactly.
;;;
;;;
;;; All this complication is to pop up a display of counts and
;;; summaries of unread messages.
;;;
;;;;
;;;; Variables for notifier and sound player processes.
;;;
;;; Configure these to your preference.
(defvar fmg-mb-player "/usr/bin/play"
"The pathname of the sound player to use. This is set to the
sox player --- works with many different file formats. If you
change this you may have to change the \"dingie\" process
invocation in the fmg-biff-hook.el file to reflect the correct
arguments for your player.")
(defvar fmg-mb-player-volume ".33"
"Volume level with 1.0 as the highest level.")
(defvar fmg-mb-sound "~/lib/sounds/emailreceived.mp3" "Path to the
sound file played by the player selected.")
(defvar fmg-mb-popup "/usr/bin/notify-send"
"Notifer to pop up the summary message. Defaults to standard
freedesktop notifier. If you change the notifier you may have
to change the \"showie\" process invocation below to reflect
the correct arguments for whatever notifier you choose.")
;;; This is not a file; it is the name of a standard icon. It is set
;;; by whatever desktop theme you may have set up (assuming you use
;;; the standard desktop notifier above). You can also use a file of
;;; your choice.
(defvar *fmg-mb-icon* "mail-message-new")
;;
;; A modified version of djcb-popup.
;; See https://emacs-fu.blogspot.com/2009/11/showing-pop-ups.html
;; Any bugs are his. :-) Actually his considerable work is gratefully
;; acknowledged.
(defun fmg-notify (title msg &optional icon sound)
"Show a popup and play a sound; TITLE is the title of the
message, MSG is the content. A default icon will be displayed and
a default sound will be played. Both can be replaced by supplying
the optional arguments."
(start-process "showie" nil fmg-mb-popup
"-i" (or icon *fmg-mb-icon*)
;; NOTA BENE!!! url-insert-entities-in-string is
;; necessary to avoid disruption by shell characters
;; such as "<" or ">". Email messages headers often
;; have these.
(url-insert-entities-in-string title)
(url-insert-entities-in-string msg))
(start-process "dingie" nil fmg-mb-player
"-q" "-v" fmg-mb-player-volume
(expand-file-name (or sound fmg-mb-sound)))
nil)
;;;;
;;; Defvars for the hook function.
(defvar *fmg-max-summaries* 10
"How many email message summaries do you want to see in the
notification?")
(defvar *fmg-folder-totals* nil
"List of totals for folders to check for new messages. This is
used so that the summary of any particular message is only shown
once unless that folder gets another new message. This keeps an
unread message from triggering the notifier over and over
again.")
(defun fmg-biff-hook (new-messages)
"Hook to add to the wl-biff-new-mail-hook. I believe this is a
new hook (at least, as of 9/10/2019 I just noticed it). It calls
its functions with the number of new emails as an argument so we
can have access to that number."
;;; debug
;;; (message "Running fmg-biff-hook with %d new" new-messages)
;; If we get called, something must have happened.
(let ((summaries "")
(show-flag nil)
(total-new new-messages)
(total-unseen 0))
(unless *fmg-folder-totals*
(setf *fmg-folder-totals* (make-vector (length wl-biff-check-folder-list) 0)))
(dotimes (i (length wl-biff-check-folder-list))
;; Do one folder.
(let ((f (nth i wl-biff-check-folder-list)))
(wl-folder-sync-entity f)
(let* ((folder (elmo-get-folder f))
(session (elmo-imap4-get-session folder)))
(multiple-value-bind (new unseen total)
(elmo-imap4-folder-diff-plugged folder)
(incf total-unseen unseen)
;;; debug
;;; (message "%d messages unread (%d new) out of %d in folder %s" unseen new total f)
(unless (= total (aref *fmg-folder-totals* i)) ; Don't re-check this folder unless
; it has a new total of messages.
(setf (aref *fmg-folder-totals* i) total)
(setf show-flag t)
;; Collect unseen message summaries from each folder into SUMMARIES variable.
(dotimes (i (min unseen *fmg-max-summaries*))
(elmo-imap4-send-command-wait session "SELECT \"INBOX\"")
(let* ((number (- total i))
(command
(format
"FETCH %d (FLAGS BODY.PEEK[HEADER.FIELDS (DATE FROM SUBJECT)])"
number))
(summary
(elmo-imap4-response-bodydetail-text
(elmo-imap4-response-value
(elmo-imap4-send-command-wait session command)
'fetch))))
(setf summaries (concatenate 'string summaries summary)))))))))
;; Display message summaries to the user.
(if (null show-flag)
(progn
;;; debug
;;; (message "No new messages")
nil)
(progn
(fmg-notify
(format "You have %d new email %s (%d unseen)"
total-new
(if (= total-new 1) "message" "messages")
total-unseen)
summaries)))))
(message "fmg-biff-hook loaded.")
;;; Local Variables:
;;; eval: (eldoc-mode 1)
;;; End:
@fgilham

This comment has been minimized.

Copy link
Owner Author

commented Sep 17, 2019

Here's a new version of my wanderlust biff hook code that plays a file and pops up a notification. My first version didn't really work; from time to time it would make Emacs loop.

This version uses what seems to be a new wanderlust hook --- wl-biff-new-mail-hook. Wanderlust calls the functions on this hook with an argument of the count of new emails seen. That simplifies the code; the only downside is that the function will keep being called as long as the new mail remains "new". So the code keeps a list of the total message count for each folder and doesn't re-check a given folder unless the count of messages for that folder changes. I'm sure there are cases where this will do something suboptimal, but it seems to work most if not all of the time.

The new version integrates with the wanderlust biff infrastructure rather than running its own timer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.