Emacs and org-mode for sending mailshots

I use Emacs for reading and sending email, so I’ve been using emacs-lisp to send mailshots for years (but in a rather clunky way).

The big shortcoming is that it is not hugely convenient getting the data (e.g., student names, email addresses, marks, comments) into emacs-lisp data structures, or conveniently writing the emails.

org-mode makes it all easier.

I present an example here: how to send mails giving feedback on performance in a test.

First we put the data in an org-mode table, giving it a name. org-mode tables are very ergonomic to enter data directly in. You could also cut and paste a CSV and convert it (insert, highlight, type “C-|”), but it’s likely better to use org-mode to enter the data in the first place.

#+NAME: assignment1
|  1 | Sleepy |    5 |
|  2 | Sneezy |   15 |
|  3 | Doc    |   10 |

Then we write the email (exactly the content of a message-mode Emacs mail-buffer, before sending), but with “%s” marking locations where we insert variable information. This is input as an org-mode EXAMPLE, with a name.

#+NAME: boilerplate
To: %s
Subject: Assignment 1
BCC: brendan.halpin@ul.ie
--text follows this line--
Dear %s,

Homework assignment 1

Your mark on assignment 1 was %s/20. In other words, you %s.


Snow White

The boilerplate defines fields to fill as:

  • full email address
  • name
  • mark, and
  • a verbal summary of the performance.

Note that the boilerplate defines the complete contents of a mail message buffer (apart from the signature, which can be inserted afterwards, as below).

The next block explicitly refers to the student data and the boilerplate (“:var boilerplate=boilerplate students=assignment1“), and then defines a function, make-message, which works on each row of the student data table and

  • creates a mail buffer, deleting its contents in order to replace them
    with our own
  • inserts the boilerplate, populating the fields appropriately
  • including creating the verbal summary based on the mark
  • renames the buffer uniquely (and marks it unmodified, to make it easy
    to delete if necessary)

The block then applies the defun to each element of the student-data list (using mapcar, which is like the apply family in R).

#+BEGIN_SRC emacs-lisp :results none :var boilerplate=boilerplate students=assignment1
(defun make-message (student)
  (delete-region (point-min) (point-max))
  (insert (format boilerplate
                  (format "dwarf%s@forest.mine" (nth 0 student))
                  (nth 1 student)
                  (nth 2 student)
                  (cond ((< (nth 2 student) 10)
                         "fluffed it")
                        ((= (nth 2 student) 10)
                         "did okay")
                        ((> (nth 2 student) 10)
                         "aced it"))))
  (insert "Snow White Housekeeping and Judgementals
The Dwarves' House
Enchanted Forest")
  (set-buffer-modified-p nil)) ;; make easy to delete for debug

(mapcar (lambda (student) (make-message student))

The end result is multiple mail buffers, one per student, each containing a mail message ready to send. You can then visit the buffers, and verify before sending. Insert the command message-send-and-exit at the end of the defun if you are truly reckless!

An example mail buffer:

To: dwarf3@forest.mine
Subject: Assignment 1
BCC: brendan.halpin@ul.ie
--text follows this line--
Dear Doc,

Homework assignment 1

Your mark on assignment 1 was 10/20. In other words, you did okay.


Snow White
Snow White Housekeeping and Judgementals
The Dwarves' House
Enchanted Forest

This is nice because we can enter the data conveniently in an org-mode table, and similarly enter the email boilerplate in an org-mode EXAMPLE block without worrying too much about escaping quotation marks etc. However, what is really nice is that we can

  1. do arbitrary things to the text using emacs-lisp (“you fluffed it”)
  2. process the data tables using, e.g,. R

It is also possible to generate arbitrarily complex HTML and multipart emails, but that’s a topic for another day.

Leave a Reply

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