← Back to all posts

Exporting Custom Slack Emoji

Published: ....
Last modified: ....

Share this post on BlueskySee discussion on Bluesky

This workflow has been proven to work at the end of March 2024, it may break in the future!

At work we recently bypassed over 60k custom slack emoji in our workspace, and I'd been meaning to download them locally so I could reference them in other discussions outside of slack with past and present coworkers.

I did some scavenging on Google (actually Perplexity) and found a fairly active Gist that seems to have captured a few different routes to accomplish the use case, but some of the API has changed since then.

So I figured I'd document what I did to accomplish this in case others are looking for a similar solution!

Steps:

Download emoji metadata:

  1. Visit your /customize/emoji page in a browser of your choosing a. This should look roughly like: https://<organization>.slack.com/customize/emoji
  2. Open the network panel and look for a request to the emoji.adminList endpoint
  3. Copy that request as fetch (right click the request, copy as -> fetch in Chrome/Arc)
  4. Copy the below snippet and paste it into the console a. This snippet only defines a few helper functions, it won't do anything malicious, but validate it for yourself if you'd like!
// Function to fetch a page of emoji
async function fetchPage(pageNum) {
  let res = TODO;
  let json = await res.json();
  return json;
}

// function to download a JSON file automatically from the console in devtools
function downloadFile(content, fileName, contentType) {
  let anchor = document.createElement("a");
  document.body.appendChild(anchor);
  let file = new Blob([content], { type: contentType });
  anchor.href = URL.createObjectURL(file);
  anchor.download = fileName;
  anchor.click();
}

// Function to batch download emoji
async function downloadAllEmoji(start, stop) {
  for (let pageNum = start; pageNum <= stop; pageNum++) {
    let json = await fetchPage(pageNum);
    let content = JSON.stringify(json);
    downloadFile(content, `page-${pageNum}.json`, "application/json");
  }
}
  1. Replace the TODO within fetchPage with the copied fetch request from the network panel
  2. Important! Look in the request body of the fetch you copied for a page: 1 entry, and replace the 1 with ${pageNum} - you'll also need to change the body to a template string!
  3. Run downloadAllEmoji with a start value >= 1 and a stop value <= the max number of pages (you can look at the emoji.adminList request for page 1 to see the maximum number of pages)

Download images:

To download the actual images, you'll want to:

  1. Move the downloaded metadata files into a shared directory
  2. Initialize a new Bun project within that directory by running bun init
  3. Paste in the following snippet into index.ts:
import fg from "fast-glob";

async function loadAllEmoji() {
  const files = await fg("page-*.json");
  const emojis = [];
  for (const file of files) {
    const json = await Bun.file(file).json();
    if (!json.emoji) {
      throw new Error(
        "Emoji not found in " +
          file +
          ", did the response get rate limited? Check the file and re-download if needed!"
      );
    }
    emojis.push(...json.emoji);
  }
  return emojis;
}

async function downloadEmoji(emojiList) {
  for (let emoji of emojiList) {
    let res = await fetch(emoji.url);
    console.log('Downloading: ' + emoji.name + ' (' + idx + 'of ' + emojiList.length + ')');
    await Bun.write(
      "./emojis/" + emoji.name + "." + emoji.url.split(".").at(-1),
      res
    );
  }
}

let emoji = await loadAllEmoji();

await downloadEmoji(emoji);
  1. Install fast-glob via bun add fast-glob
  2. Run the above script via bun ./index.ts

Note! If you have a lot of emoji, this will take a while! It downloads the images synchronously to avoid being rate limited.

Recommendations:

I recommend not trying to run through all pages at once when fetching the emoji metadata, as sometimes Slack may not be able to handle the requests.

In my case, since we had ~600 pages to go through, I started with chunks of 25 then went up to 100 pages at a time.

Tags:

Related Posts

Tip

Roundup Notes in Obsidian with Dataview

Published: ....

A quick tip for creating roundup or summary notes based on other notes in Obsidian using the Dataview plugin!

Quick Tip - Theme Aware Images

Published: ....

Have you ever found the need to change the image you render on a web page based on the current preferred color scheme of your theme?

Async Class Creation In JavaScript

Published: ....

Have you ever wanted to create a class in JavaScript or TypeScript but also have the initialization be async? Here's a quick tip on a pattern that I've used in the past!

Server Side Rendering Compatible CSS Theming

Published: ....

A quick tip to implementing CSS theming that's compatible with Server Side Rendered applications!

Replacing Dropbox Capture with Raycast

Published: ....

Recently Dropbox announced that it was shutting down the Capture app/service, so I sought out an alternative that provided a similar user experience!

Quick Tip - Specific Local Module Declarations

Published: ....

A quick tip outlining how to provide specific TypeScript type definitions for a local module!

Bluesky Tips and Tools

Published: ....

A (running) collection of Bluesky tips, tools, packages, and other misc things!

The Bookkeeping Pattern

Published: ....

A quick look at a small but powerful pattern I've been leveraging as of late!

Don't Break the Implicit Prop Contract

Published: ....

React components have a fundamental contract that is often unstated in their implementation, and you should know about it!

A Better useSSR Implementation

Published: ....

Replace that old useState and useEffect combo for a new and better option!

Tip: Request and Response Headers

Published: ....

There's a common gotcha when creating Web Request and Response instances with Headers!

Custom Favicon Recipes

Published: ....

Two neat tricks for enhancing your site's favicon!

Fixing Zed's language server

Published: ....

Zed language server quick tip; fixing Zed's language server

Guides