How I Made a Max Slack Emoji Bot

I made a Slack bot. Her name is Max, and she maxes out Slack's character count with emoji.

Working Poop Bot!

The Idea

While iterating over a few design ideas at work, a friend (sarcastically) Slacked me, "you’re always shitting over everything I do". I had been shooting down his ideas a lot... so I couldn't blame him. Instead, I responded in a mature and adult way: I sent him as many 💩s as Slack would allow in a single message.

It took me a few attempts of trial-and-error to get the number maxed out, and that trial and error was too much! So I decided then and there I would make a thing to spam my coworkers with maximum emojis as easily as possible!

Some Testing

I quickly did some tests by typing stuff at @slackbot, and learned the following:.

  1. The maximum number of characters allowed in a user-sent Slack message is 4,000.
  2. Slack counts the characters of the string token :poop: (or :shit: or :hankey:, whichever you use), not the unicode string, or some other representation.
  3. You don't have to put spaces between each 💩. So :poop::poop: saves one character over :poop: :poop:.
  4. :v: (✌) is the shortest Slack emoji string token, and :diamond_shape_with_a_dot_inside: (💠) is the longest.

First Prototype

Once I knew some limits, I figured I could make myself a webapp. So I went to CodePen, remembered my jQuery skills, and made a searchable list of emoji. @efburke had an idea that was loosely/vaguely/kinda related to this which may or may not have partially been the impetus for this implementation. He also abused his /max :troll: privileges, and effectively ruined one of my favorite private slack channels. So there, are you happy now, Eugene?!

See the Pen Max Slack Emoji by Cory Chainsman (@corychainsman) on CodePen.

This prototype works, and has some nice features! The emoji attributes are pulled in as JSON from Gemoji, then selectively shown when you type in the search box. You can search by emoji code (:poop:), or by associated tags or aliases (pile of poo or crap). You can click the smiley face in the search field to get an emoji picker courtesy of the WDT Emoji Bundle. The results include the number of emoji that will fit within the specified character limit, with a little bit of code styling to seem more Slack-like. You can hover over a result and click Copy to copy the string containing the maximum number of emoji string tokens. (e.g. :poop::poop::poop::poop:... and so on)

This prototype also has some issues, mostly around the 4,000 max character count. Most egregiously, it takes ~17 seconds to load on my computer, and the vast majority of that is in generating the HTML elements from the JSON. Then, while searching, the page lags because filtering through and hiding/showing so many DOM elements is slow. So I artificially lowered the character count down to 40 for development. Also, some emoji like :v: (✌) didn't render right, others like :heart: (❤️) didn't render at all, and some are just plain ol' undefined! This may be an issue with my local machine's Apple Color Emoji font, but it's still an issue.

Overall, this was an acceptable first proof of concept, but if I wanted to make spamming coworkers as easy as possible, I needed to cut out the extra cruft! No more opening a website, searching for your emoji, then pasting the result back into Slack. I needed a Slack API integration.

Slack Bot

Even at the beginning, I knew I really wanted the solution to be a Slack Slash Command. After all, what could enable faster emoji-spamming than /max :poop:?

I'd never made a Slack integration before, and other than a quick-and-dirty foray into Pinboard/Github, I haven't made much that needed to continually interact with a REST API.

So I figured I'd use an existing framework. Enter Botkit: "Building Blocks for Building Slack Bots". I mucked around with Botkit for a while and found it really difficult to get set up for a relative novice like myself. In general, it was more complicated than I needed. I really just need a single function.

In semi-pseudo-code.js, it's:

var respond = function(emoji_text) {  
  var multiply = Math.floor(4000/emoji_text.length);
  var responseText = Array(multiply+1).join(emoji_text);
  response.send(responseText);
}

Wait! This seems like a job for AWS Lambda! According to Wikipedia

"AWS Lambda is a compute service that runs code in response to events and automatically manages the compute resources required by that code."

And Lambda even has relatively new Slack bot blueprints! What luck! And the blueprints come with pretty good guidelines around how to set up Slack's servers for your slash command.

After some putzing around with homebrew (for awscli), I finally had my Lambda function up and running in actual-code.js, and here's what it looks like:

The Code
var qs = require('querystring');  
var token = "m4d3Upc0d3H3r3"; //this is not my real token

exports.handler = function (event, context) {  
    if (token) {
        processEvent(event, context);
    } else {
        context.fail("Token has not been set.");
    }
};

var processEvent = function(event, context) {  
    var body = event.body;
    var params = qs.parse(body);
    var requestToken = params.token;
    if (requestToken !== token) {
        console.error("Request token (" + requestToken + ") does not match expected");
        context.fail("Invalid request token");
    }

    var commandText = params.text;

    var multiply = Math.floor(4000/commandText.length);
    var responseText = Array(multiply+1).join(commandText);

    if (commandText.charAt(0) === ":" && commandText.charAt(commandText.length-1) === ":") {
    context.succeed(
    {
        "response_type": "in_channel",
        "text": responseText
    }
        );
    } else {
        context.succeed("`" + commandText + "`? Seriously?! Use a *real* emoji like `:poop:`")
    }
};

This code checks my Slack slash command token, and if it matches, checks if the passed string begins and ends with :. If so, the bot responds publicly in-channel with the maximum number of emoji that fit below 4,000 characters. If not, it responds privately in channel with an error telling you to use a real emoji.

My emoji checking if-statement is an acceptable-if-simple way of checking if a string is an emoji. It gives false negatives for Unicode emoji codes, and false positives for any non-emoji strings that are wrapped in colons like :this one, for example:. Importantly, since Lambda charges me for compute time as well as for each request, my simple if-statement is a computationally easy way to check for emoji, as opposed to storing or requesting a huge list of all acceptable emoji and checking against it. So if the stray /max :poooooooP: gets past, or the stray /max 💩 gets missed, I think it's okay.

The Result

Now that it's all set up, I can simply type /max :poop: into any slack channel and get back this:
Working Poop Bot!

Humorously you get 666 :poop:s in 4,000 characters.

Actually, the 4,000-character limit is only on user-entered messages; bots can post up to 16,000 characters, though Slack recommends staying below 4,000 for bots, too. I had my limit up at 16,000 for a while, but it REALLY slowed down the Slack interface, so I guess they know what they're talking about when they recommend 4,000.
SUPER MAX Poop Bot!

Extra Fun

Because my emoji-checking if-statement is pretty loosey goosey, it allows for some nifty stuff. For instance, skin tone additions like :clap::skin-tone-6: are accepted to get different melanin levels in the mix. Custom emoji can be uploaded to Slack, and they get their own custom string tokens, so a more lenient parser accepts those as well.

Max Troll Face

Also, it allows for multiple emoji in a row, so you can get super fun patterns like these:

Skin Tone Diagonals Moon Phase Diagonals

The Future

In the future I'd like to add a few improvements:

  • Better emoji checking
  • Secured token storage...
  • Better local dev environment using npm's lambda-local
  • Package it into a distributable Slack App
  • Thanks to an idea from @jdherg, I'd like to add the ability to specify /max big [emoji] to get the maximum number of large emoji before Slack displays them as small emoji (23 is the max number of big emoji, but it is irrelevant of the number of characters in the string token)