EmacsConf: Using message-sent-hook to run buffer-specific code to keep track of e-mailed schedules
| emacs, org, emacsconf
I make lots of little EmacsConf schedule updates based on the speakers' feedback.
I needed a way to keep track of the schedule that I've just sent the speaker so that I could calculate the differences and notify them again the next time I change the schedule for their talk.
I like the way that keeping track of this lets me include a summary of the change in the subject and in the body of the message. For example, one of the emails I sent today had the subject:
This blog post is about updating the EMAILED_SCHEDULE entry property in my Org Mode file that keeps track of this year's conference talks so that I can save the schedule that I've emailed. I want to set it only after I've successfully sent the email, because I might change my mind or forget to send the e-mail, and I want to make sure the entry property is based on what I've e-mailed.
EmacsConf 2026: Schedule update as of 2026-10-28: svg: Sun ~2:20 PM EST (40 min earlier)
I send the messages within Emacs, of course. This lets me use emacsconf-mail.el to mail-merge using Emacs Lisp. I can then use message-sent-hook to run buffer-specific code once I've sent the message. This lets me set it up so that each message updates its own talk once I've used C-c C-c to send it.
First, I need a function that adds to a talk's logbook.
This is not specifically needed for the message-sent-hook, but it's useful for keeping track of the fact that I've sent this kind of email to that speaker on this date.
It's very helpful to be able to see who I've already sent the backstage information to or when was the last time I sent them a schedule update.
I can also filter the list of talks by the text in the logs so that if I partially send a mail merge and then cancel the rest because I want to tweak the code, I can send the new mail merge only to the remaining speakers.
This is in emacsconf.el.
emacsconf-add-to-talk-logbook: Add NOTE as a logbook entry for TALK.
(defun emacsconf-add-to-talk-logbook (talk note) "Add NOTE as a logbook entry for TALK." (interactive (list (emacsconf-complete-talk) (read-string "Note: "))) (save-window-excursion (save-excursion (emacsconf-with-talk-heading talk (emacsconf-add-to-logbook (if (functionp note) (funcall note) note)) (bury-buffer (current-buffer))))))
This function is the one that gets added to the message sent hook. It's in emacsconf-mail.el:
emacsconf-mail-add-to-logbook: Add to logbook for all matching talks from this speaker.
(defun emacsconf-mail-add-to-logbook (email note) "Add to logbook for all matching talks from this speaker." (interactive (let* ((email (mail-strip-quoted-names (plist-get (plist-get (notmuch-show-get-message-properties) :headers) :From))) (talks (emacsconf-mail-talks email))) (list email (read-string (format "Note for %s: " (mapconcat (lambda (o) (plist-get o :slug)) talks", ")))))) (condition-case e (save-window-excursion (mapc (lambda (talk) (emacsconf-add-to-talk-logbook talk note)) (emacsconf-mail-talks email))) (error (message "Error %s" e))))
And this function actually sets up the message-sent-hook:
emacsconf-mail-log-message-when-sent
(defun emacsconf-mail-log-message-when-sent (o message) (add-hook 'message-sent-hook `(lambda () (save-window-excursion (emacsconf-add-to-talk-logbook ,(plist-get o :slug) ,message)) (when (match-buffers "*unsent") (switch-to-buffer (car (match-buffers "*unsent"))))) nil t))
Which gets called by this part of emacsconf-mail-prepare:
(when (plist-get template :log-note)
(mapc (lambda (talk)
(emacsconf-mail-log-message-when-sent talk (plist-get template :log-note)))
(emacsconf-mail-talks email)))
Then my emacsconf-mail-interim-schedule-update can specify the :log-note like this:
:log-note
(lambda ()
(dolist (talk (cdr group))
(emacsconf-with-talk-heading talk
(emacsconf-schedule-save-emailed-time)))
(concat "emacsconf-mail-interim-schedule-update"
(if log-note (concat ": " log-note) "") " - " summary))
This lets me run that code only when the mail gets sent.
Next automation idea: Someday I may even figure out how to attach an iCalendar file (including updates to events) so that people can add it to their calendars.
If you find yourself wanting to run code when a message gets sent, then definitely take a look at message-sent-hook.
You can add a buffer-local hook with the fourth argument to add-hook (ex: (add-hook 'message-sent-hook (lambda () ...) nil t)) so that you can take actions specific to that message.
Have fun!