Capturing slack messages directly into Emacs orgmode inbox

Srijan Choudhary Srijan Choudhary
- 3 min read
Screenshot of a custom org capture button in Slack message popup
Org capture button in Slack message popup

Ever since switching to orgmode as my GTD system, I've been trying to (slowly) optimize my capture system for things coming to me from different places. One of the things that I was not satisfied with was capturing and processing inputs from Slack.

Slack messages can easily get lost in the fast-paced stream of conversations. Manually copying and pasting messages introduces too much friction, and also removes some context (backlinks), unless a link to the original message is also manually copied. There are ways to save messages in Slack itself, but it just makes it another Inbox to check, and I'm trying to reduce that.

I started by using the saved messages feature to save messages in a single place, and later either shifting them to my org inbox manually, or directly working on them and completing them. Then, I shifted to using the Slack Todoist integration to save slack messages to Todoist, and pulling them from Todoist into my org Inbox.

I've now found a better mechanism that allows me to seamlessly capture Slack message with it's context into orgmode. Here's a demo:

Demo video showing a custom slack button to trigger org capture with the selected message's contents and a link back to the message

Demo breakdown

This method uses userscripts to add a custom button to the hover menu that comes up for any message in slack, and triggers org protocol capture with the message details when the button is clicked. The message details include the sender, message text, and a direct link back to the message. I've set up this protocol handler to ask me to enter the heading of the capured item, but it can be as easily set up to directly capture the message without user input.

Setup and Implementation

Prerequisites

  1. Slack running in a browser (instead of a desktop app)
  2. Browser extention for userscripts (Tampermonkey, Violentmonkey, Greasemonkey, etc)
  3. Emacs with orgmode installed
  4. Org protocol setup

I didn't find a good way to inject userscripts into the Slack destop app, so for now, this method requires using Slack in a browser. It also works when Slack is installed using the "Install Page as App" feature of the browser.

Setting up Org Protocol Capture

Setting up org protocol capture involves two steps: configuring your OS to use Emacs to open org-protocol:// links, and configuring Emacs to save the captured data as you want.

For OS setup, please check the guides for your OS here: https://orgmode.org/worg/org-contrib/org-protocol.html#orge00964c

On Emacs side, this is the minimal config required:

(server-start)
(setq-default org-agenda-files '("~/org"))
(setq-default my-org-inbox
    (expand-file-name "inbox.org" "~/org"))
(setq-default org-capture-templates
      '(    
    ("i" "Inbox" entry (file my-org-inbox)
         "* %?\n%i\n%U"
         :kill-buffer t)
    ("l" "Inbox with link" entry (file my-org-inbox)
         "* %?\n%i\n%a\n%U"
         :kill-buffer t)))
(setq-default org-protocol-default-template-key "l")
(require 'org-protocol)

An optional enhancement that can be seen in the demo is: open a new emacs frame to capture this message, then close it automatically after the capture has been done. For this, I use the config snippet from prot's excellent post: https://protesilaos.com/codelog/2024-09-19-emacs-command-popup-frame-emacsclient/

To run emacsclient with the popup frame parameter, I use:

emacsclient --create-frame -F \
    '((prot-window-popup-frame . t) (name . "org-protocol-capture") (width . 80))' \
    -- %u

Emacsclient's args can be set in the desktop entry file (linux) or org-protocol.app file (OSX) when setting up org-protocol.

The userscript

For the userscript, I searched for an existing published userscript for Slack that adds a button, then tweaked it a bit to configure the button according to my needs.

I've published the userscript here: https://greasyfork.org/en/scripts/521908-slack-org-protocol-capture

From this, I learned about an interesting API called MutationObserver that seems very useful for userscripts.

Notes

Using emacs-slack

Another simple approach for this can be to use the emacs-slack package to directly use Slack from Emacs. I tried this, and it does not work very well for me because:

  1. My org limits Slack session authentication to 1 week, and authenticating in emacs-slack is a little cumbersome.
  2. We also use Slack huddles, and it does not work well with emacs-slack.

Possible Improvements

  1. Make org capture template configurable
  2. Add tooltip to the button
  3. Pass even more metadata like message timestamp, channel name, emojis, etc.
  4. Maybe some keyboard shortcuts to trigger the capture?

Limitations / Drawbacks

Since this is dependent on the HTML structure of the slack web app, it can be fragile and can break easily if there are changes in the app.

This userscript uses the MutationObserver API to observe DOM changes in the whole slack workspace. So, it has some impact on performance. However, I've been using this daily for the last several months and I have not noticed any issues.

Interactions

👍 8 likes

🔁 2 reposts