Fetch e-mail with mbsync in Emacs with a hydra to choose the channel
E-mail is my preferred way of communication, even if some say that e-mail is doomed.
I read my e-mail with Gnus in Emacs. The IMAP back-end in Gnus can sometimes be slow over the network and therefore lock Emacs while fetching. Another drawback of IMAP is the need to be on-line to access the e-mails on the server. Finally, having a local copy of all my messages is important for e-mail provider independence.
This is why I use mbsync/isync to get my e-mail for several accounts. This can happen outside of Emacs by calling the mbsync
command which will use the appropriate configuration in .mbsyncrc
to do its thing. I could put this on a cron job, but I prefer to run the e-mail fetching at my pace. Since my interface to the computer is Emacs, I want to be able to use mbsync
from Emacs.
It shouldn't be difficult to write an Elisp function to call an external process running mbsync
, but Dimitri Fontaine put together mbsync.el which does this cleanly logging output to a dedicated buffer and warning if things go wrong when trying to connect to servers.
Unfortunately, mbsync.el
runs mbsync
with the -a
flag, which means that all channels (that is the e-mail accounts configured in .mbsyncrc
) will be fetched. While this is a sane default, I sometimes want to fetch just one e-mail channel. The author of mbsync.el
had the nice idea of providing a customizable variable to store the flags:
(defcustom mbsync-args '("-a") "List of options to pass to the `mbsync' command." :group 'mbsync :type '(repeat string))
We can therefore exploit this to change the flags. I wrote the following function to call mbsync
for a particular channel:
(defun gd/mbsync-single-channel (channel-name) "Fetch e-mail with mbsync for a single channel" (interactive "smbsync channel: ") (message "Fetching mail for channel %s" channel-name) (let ((mbsync-args (list channel-name)) (mbsync-buffer-name (format "*mbsync %s*" channel-name))) (mbsync)))
The function takes a string containing the channel name and will use it as the flag to call mbsync
. I also change the name of the the buffer where mbsync.el
will write the logs (mbsync-buffer-name
is a variable defined in mbsync.el
). Binding the variables with let
leaves them as they were before calling the function.
This function can be called as an interactive command (with M-x
), but can also be used to define one function for each e-mail channel:
(defun gd/mbsync-proton () (interactive) (gd/mbsync-single-channel "proton")) (defun gd/mbsync-fastmail () (interactive) (gd/mbsync-single-channel "fastmail")) (defun gd/mbsync-gmx () (interactive) (gd/mbsync-single-channel "gmx")) (defun gd/mbsync-garjola () (interactive) (gd/mbsync-single-channel "garjola"))
I am not very happy with this approach, since there is lots of redundancy. Maybe I could use some macros to simplify this, but I am not proficient enough in Elisp yet.
With these functions, I can define a hydra which will provide a menu to select the e-mail channel to fetch.
(defhydra gd/hydra-mbsync (:timeout 4) "Fetch e-mail with mbsync" ("p" gd/mbsync-proton "proton") ("f" gd/mbsync-fastmail "fastmail") ("x" gd/mbsync-gmx "gmx") ("g" gd/mbsync-garjola "garjola")) (global-set-key (kbd "<f9> O") 'gd/hydra-mbsync/body)
Now, when I type <f9> O
, the following menu appears in the mini-buffer.
And I can select which channels to fetch. Yes, channels in plural, because this is a persistent menu which stays there for 4 seconds (the timeout parameter of the hydra) and I can select several of the channels.