slag: A terminal client for Slack.

This was a project I worked on with my good buddy Brandon Fujii. You can find the code for slag on Github here or see the npm module here.

The Problem

Up until last year, I was doing all my work on a late 2008 13” Macbook with eight gigs of ram. It wasn’t impossible to get things done, but there were some tasks which took a long time or would start my fan spinning (like launching Illustrator or watching videos on YouTube).

There was one thing, though, that absolutely destroyed me every time I opened up my laptop: Slack. I use Slack as my go-to communication app for student groups, which meant that I was liable to be signed into around five Slack teams at any point in time.

The Slack desktop app is build on Github’s Electron, which in turn uses Chromium on the front-end. Chromium is pretty notorious for being a memory hog, so it’s not hard to see how that could affect desktop applications built on it.

This is a screenshot taken on my current work machine, a late 2014 15” Macbook Pro with 16 gigs of ram. At the time this screenshot was taken, I had Slack open for about three minutes. It’s already using up more than a gigabyte of my memory.

Our Approach

From the beginning, the goal of slag was never to replace the Slack app completely–after all, the apps are great. What we wanted was a stripped down version of Slack that was much lighter. Something a user would be able to use on a day-to-day basis for the majority of Slack communications. A client that provided 80% of Slack’s functionality using 20% of the memory.

This client would ideally provide the essentials of Slack as well as a small subset of the niceties. So while we first set out to implement the larger parts of the Slack app (the messaging, team and channel switching, etc), we also recognized that a lot of the joy of using Slack comes from the little things (emoji, @-mentions, /-commands), and we wanted to recreate those as well.

Thankfully, Slack provides a very helpful web API that had almost everything we needed to build out the messaging part. For the ncurses-like terminal GUI, we used a Javascript library called blessed, along with some components from blessed-contrib and a few custom components as well.

A Few Challenges

/-commands like giphy aren’t exposed in Slack’s public web API, but by looking at network calls in a browser we were able to figure them out. @-mentions required some regex expressions to decipher and assemble. Supporting Slack’s colon-flanked :emoji-code: was a little more of an exercise since Javascript pre-ES6 doesn’t support unicode, but we were able to get it working using surrogate pairs.

With the actual client working, we then worked on improving usability. We put the client on npm to make it easier to install. Since this was my first npm module, I learned a ton about publishing to npm and writing npm scripts.

We also realized that adding tokens (i.e. providing credentials) for a team was still a pain. We added some useful commands (namely, slag --set-token <token> and slag --set-tokens <json-file>) to make that process easier as well as a helpful manual for new users.

The Result

When all was said and done, we ended up with a Slack terminal client that supported

Some things are a little more difficult to implement in a text-based GUI. Among those are:

We also measured our memory usage. This is for a slag client signed into four teams, although results should be comparable across more or fewer team sign-ins.

Next Steps & Retrospective

We’re currently working on transpiling slag over from ES6 so it can be used with older versions of Node. If I had the chance to start this project over again, I probably would’ve paid a little more attention to making sure that transpiling would be easier down the road. While we did get to use some nifty ES6 features to handle the asynchronous API calls, it would’ve also been nice to think about accessibility from the get-go.

Note: Slack has since made some improvements to the client by moving certain teams over to a “minimal client” which a much smaller memory footprint. The full client, however, appears to use the same amount of memory.