Sending Email from Org Mode

Every so often, I need to generate a bunch of form emails. Often, I’m sending the same email to multiple groups of people and I want use variables, such as $first_name, in the text. While I’m sure there a number of online services which could achieve this for me, I really don’t need to set up a fancy campaign or use tracking. Plus, I’m wary of submitting others’ emails to a third party.

Of course, I’ve used Emacs to do this, using org mode and AppleScript.

Overview

To approach this task I start with a single org file. Getting AppleScript executing from an org block is trickier than I would like. Instead, I write the script whole cloth in my org file and tangle it out to a separate .scpt file. I store the data in my file as a table. I then use a bash block, which takes the data through :var, to loop over running the script.

AppleScript for Email

I’ve never formally learned AppleScript, but some searching on StackOverFlow and trial-and-error can get you pretty far. Mac OS X has a built in editor found in /Applications/Utilities/Script Editor. Once you’ve opened the editor, you can browse the dictionary with File > Open Dictionary.... This dictionary lists all the classes and commands for different applications. The AppleScript language itself looks like natural language and is relatively easy to understand… especially once someone else has properly constructed the program.

For my email-sender script, I store the AppleScript source is as a block. This block is tangled (C-c C-v C-t) to a .scpt file. The first half of this script sets some variables, including the body of the email message. The second half creates the message and sets a “to” recipient (could also set a “cc” recipient).

#+NAME: script
#+BEGIN_SRC apples :tangle email.scpt
on run argv
   set varName  to item 1 of argv
   set varEmail to item 2 of argv
   set varBody  to "Hello " & varName & ",

Blah blah blah. Lor ipsum, etc.

Sincerely,
Elsa"

   tell application "Mail"
        set theMessage to make new outgoing message with properties {subject: "Email", content: varBody, visible:true}
        tell theMessage
             make new to recipient with properties {name:varName, address:varEmail}
             #send
        end tell
   end tell
end run
#+END_SRC

Note that I’ve commented out the send command and set the message property visible: true. This means that the draft emails appear in the Mail GUI, but do not get automatically sent. This allows me to do trial runs before spamming my recipients.

Filling the Variables

Once I have the script and email body written, I know what variables I’ll need to fill in. The next step is to put all the data into a named org table. I usually do this data formatting by hand since I’m bringing together data items from many different sources.

#+NAME: data
|    Name    |      Email      |
|------------|-----------------|
| Mx. Person | person@test.com |

Since I tangle the email sending AppleScript program, I need to run it from the command line. I call the script once for each row in the data table using a bash loop and variable arrays. Then, I use C-c C-c to run the bash loop and generate the emails.

#+BEGIN_SRC bash :var name=data[,0] email=data[,1]
for i in `seq 0 0`; do
osascript email.scpt "${name[$i]}" "${email[$i]}"
done
#+END_SRC

Note that I do not calculate the total number of data items / rows. That would take way too much bash magic for this simple example. Also, if you want to test the variables with an echo statement, use :results output as an argument to the org block.

Hitting Send

I usually choose to manually hit send on each individual email once I’m satisfied with the script.

I keep all of these org commands in a single notes.org file so that I can remember how to recreate the emails (usually a year later when it’s time to send them again). For example, this notes.org file sends introduction emails for a mentoring program I organized with Women in HPC.


Comments

Comments are sent directly to me and may or may not be posted for display on this page.