Thursday, June 19, 2025

Posting Org-Mode captures to Mastodon

 I keep my personal reading log in an Emacs org-mode table. It's nice and small, and works on every computer I use, and thanks to org-mode's spreadsheet feature, I can even do some basic analysis of it. I use a fancy capture template to collect the information about each book and store it at the end of the log without having to open the log file, navigate to the end of the table, and tab through the various fields.

Since it's also possible to post to the Mastodon microblogging platform from Emacs thanks to the mastodon.el library, I figured it shouldn't be too difficult to automatically post to mastodon every time I start reading a new book. And guess what! I was right.

This relies on the fact that org-mode provides "hooks" to customize the note capture process. A hook is a lisp function that gets run by emacs whenever the corresponding event takes place. In this case, org-mode provides several hooks related to finishing up the note capture process. The normal hooks are "org-capture-before-finalize-hooks", which run before org-mode cleans up everything related to capturing the note and gives you access to the captured note, and "org-capture-after-finalize-hooks", which runs at the very end of the note capturing process, which org-mode describes as "Suitable for window cleanup." These hooks run every time you capture any kind of note, but recent versions of org-mode also provides a way to define "before-finalize" and "after-finalize" hooks that only run when a particular capture template is used, so that's what I did.

At the end of my existing "make a note about starting to read a book" template, I added the extra option

:before-finalize #'djf-org-share-reading

 which tells org-mode to run the hook djf-org-share-reading only when the "start reading a book" template is launched, and then I defined the function djf-org-share-reading:

(defun djf-org-share-reading ()
  (when (require 'mastodon nil t)
    ;; parse out the org-capture data
    (let* ((capture (buffer-substring (pos-bol) (pos-eol)))
           (split-line (split-string capture "|+" t " +"))
           (author (car split-line))
           (title (cadr split-line))
           (language (caddr split-line)))
      (save-excursion
        ;; Create the mastodon toot buffer, insert the captured
        ;; Org information, and then post it.
        (mastodon-toot)
        (insert (format "I just started reading %s by %s%s"
                        title author (if (string-equal language "F") " in French" "")))
        (mastodon-toot-send)))))

 First, I make sure that mastodon is available and loaded, then I grab and process the information about the book that I'm reading, then I call mastodon-toot to set up a buffer for creating a post, insert the message that I want to post, and then tell mastodon.el to send the post. If I haven't logged into Mastodon from Emacs yet, the call to "mastodon-toot" will automatically log me in. Mastodon takes care of the posting and getting rid of the buffer holding my draft post, and once djf-org-share-reading returns, org-mode cleans up the buffer it used to save the note, and I'm right back where I started in Emacs before I made the note.