4.4.2 Sending Webmentions

At this point, assuming the site author has made & recorded one or more webmentions (see made & recorded), they presumably want to send them.

Despite the protocol’s simplicity, sending Webmentions is a non-trivial task. The sender needs to carry-out Webmention endpoint discovery on their targets (which itself involves fetching & parsing the resulting document). Then there’s the question of handling failures to send & re-trying. Finally, since Webmention processing is generally done asynchronously, the sender has to “check back” to get the final result.

Aaron Precki has built a service to handle this: Telegraph. Once you sign-up (using See IndieAuth, of course), you can send a Webmention with a single API call over HTTP.

indie-org simplifies this further with indie-org-webmentions-send a function that takes a source, target, and Telegraph API token and will send the Webmention (recall that the precise sort of Webmention is not encoded in the protocol; the receiver discovers it by parsing the sender’s post).

We can record sent Webmentions in indie-org-webmentions-sent, another attribute of indie-org-publication-state. The process of taking both & determining what Webmentions are to be sent is encapsulated in indie-org-webmentions-required.

So equipped, we can now code-up a function in our site’s Lisp that we can call post-publication that will handle sending all new Webmentions:

(defun iosh/send-webmentions (prod)
  "Send webmentions post-publication.
PROD shall be set to t for production and nil for staging."
  (indie-org-enable)
  (let* ((env (if prod :prod :staging))
         (all-pub-states
          (if (file-exists-p publication-state-file)
              (indie-org-state-read publication-state-file)))
         (publication-state
          (or (plist-get all-pub-states env)
              (indie-org-state-make)))
         (webmentions-made
          (or (indie-org-state-v2-webmentions-made publication-state)
              (indie-org-webmentions-make-made)))
         (webmentions-sent
          (or (indie-org-state-v2-webmentions-sent publication-state)
              (indie-org-webmentions-make-sent)))
         (webmentions-to-send
          (indie-org-webmentions-required webmentions-made webmentions-sent))
         (token
          (indie-org-read-token
           telegraph-io-token-file))
         (now (current-time)))
    (while webmentions-to-send
      (let* ((wm (car webmentions-to-send))
             (location
              (if prod
                  ;; The source is just the page key
                  ;; (e.g. "blog/foo.html")-- it is only here that we
                  ;; know we're sending this for prod, so prepend the
                  ;; "https://indie-org.sh" here:
                  (let ((src (car wm))
                        (dst (cdr wm)))
                    (indie-org-webmentions-send
                     (cons (concat "https://indie-org.sh" src) dst)
                     token))
                (message "Will send webmention: %s :=> %s" (car wm) (cdr wm))
                nil)))
        (indie-org-webmentions-record-sent
         wm
         now
         webmentions-sent
         location))
      (setq webmentions-to-send (cdr webmentions-to-send)))
    (setf (indie-org-state-v2-webmentions-sent publication-state) webmentions-sent)
    (plist-put all-pub-states env publication-state)
    (indie-org-state-write all-pub-states publication-state-file)))