<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wc="https://wilhelm.codes/ns/feed/1.0"><channel><title>Wilhelm Codes · Emoji</title><link>https://wilhelm.codes/tags/emoji/</link><description>Slinger of nibbles, bits and bytes. Over 25 years of professional experience as a software engineer. Love making glowing rectangles go "beep-boop".</description><generator>Hugo</generator><language>en-US</language><managingEditor>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</managingEditor><webMaster>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</webMaster><lastBuildDate>Wed, 28 Sep 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://wilhelm.codes/tags/emoji/index.xml" rel="self" type="application/rss+xml"/><item><title>Why Can't I Hold All These Slack Emojis?</title><link>https://wilhelm.codes/blog/why-cant-i-hold-all-these-slack-emojis/</link><pubDate>Wed, 28 Sep 2022 00:00:00 +0000</pubDate><author>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</author><guid>https://wilhelm.codes/blog/why-cant-i-hold-all-these-slack-emojis/</guid><category>slack</category><category>emoji</category><category>bash</category><category>api</category><wc:kind>post</wc:kind><description>Previously, last blog post I wrote covered how to make a quick escape with your precious hoard of custom Slack emojis. It was fairly well-received, but didn&amp;rsquo;t quite cover the next step in the migration process; how do you upload your millions of little images to your new Slack workspace?</description><content:encoded><![CDATA[<p>Previously, <a href="https://wilhelm.codes/liberating-custom-slack-emojis">last blog post</a> I wrote covered how to make a quick escape with your precious hoard of custom Slack emojis. It was fairly well-received, but didn&rsquo;t quite cover the next step in the migration process; how do you upload your millions of little images to your <em>new</em> Slack workspace?</p>
<h2 id="gathering-requirements">
  <a class="heading-link" href="#gathering-requirements">Gathering Requirements<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>Since we&rsquo;re doing some destructive operations, we&rsquo;ll need to make sure our Slack user has the appropriate permissions to manage emoji; adding &amp; deleting. This means using a different section of the Slack interface which uses an entirely seperate set of API endpoints.</p>
<p>You&rsquo;ll still be using the following, which can be found using instructions from <a href="https://wilhelm.codes/liberating-custom-slack-emojis#heading-some-investigative-work">the previous article</a>:</p>
<ul>
<li>An API request token.</li>
<li>A session cookie.</li>
<li>A workspace, or team, id.</li>
</ul>
<p>In addition, you will need your workspaces&rsquo;s subdomain, or URL. This can easily be found within the Slack app itself:</p>
<p><img
  src="https://wilhelm.codes/blog/why-cant-i-hold-all-these-slack-emojis/image-1_hu_dacba861f5d90b40.webp"
  srcset="/blog/why-cant-i-hold-all-these-slack-emojis/image-1_hu_dacba861f5d90b40.webp 607w"
  sizes="(max-width: 48rem) 100vw, 736px"
  width="607"
  height="490"
  alt=""
  
  loading="lazy"
  decoding="async"
/>
</p>
<h2 id="testing-your-findings">
  <a class="heading-link" href="#testing-your-findings">Testing Your Findings<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>This is an incredibly straight-forward process as it&rsquo;s quite similar to what we already know. The primary difference is this is a <code>multipart/form-data</code> upload. So, instead of shipping of a JSON payload, it&rsquo;s a form with associated fields.</p>
<p>With the information gathered above, you can upload directly to Slack using a simple cURL command like so:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s --compressed <span class="s2">&#34;https://&lt;domain&gt;.slack.com/api/emoji.add&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -H <span class="s1">&#39;content-type: multipart/form-data&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -H <span class="s2">&#34;cookie: d=&lt;cookie&gt;;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -F <span class="s2">&#34;token=&lt;token&gt;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -F <span class="s2">&#34;name=&lt;name&gt;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -F <span class="nv">mode</span><span class="o">=</span>data <span class="se">\
</span></span></span><span class="line"><span class="cl">  -F <span class="s2">&#34;image=@&lt;local-emoji-path&gt;&#34;</span></span></span></code></pre></div>
</div>
<p>Replace the following:</p>
<ul>
<li><code>&lt;domain&gt;</code> is your workspace&rsquo;s, or team&rsquo;s, private URL.</li>
<li><code>&lt;cookie&gt;</code> is your session cookie value.</li>
<li><code>&lt;token&gt;</code> is your request token.</li>
<li><code>&lt;name&gt;</code> will be the named reference of your new emoji.</li>
<li><code>&lt;path&gt;</code> is the relative, or absolute, local path of your emoji file to upload.</li>
</ul>
<p>The JSON response to this request will have the following structure if all goes well:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">JSON</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> <span class="nt">&#34;ok&#34;</span><span class="p">:</span> <span class="kc">true</span> <span class="p">}</span></span></span></code></pre></div>
</div>
<p>And, if something went wrong:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">JSON</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span> 
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;ok&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;error&#34;</span><span class="p">:</span> <span class="s2">&#34;err_code&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
</div>
<p>That&rsquo;s it! Now that we know how to upload from the command line.</p>
<h2 id="the-implementation">
  <a class="heading-link" href="#the-implementation">The Implementation<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>If you&rsquo;re only uploading a handful of emojis, it might make sense to just do it via Slack&rsquo;s UI. However, this can get a bit tedious if you have dozens, or even hundreds, to upload.  We can  automate things even further by:</p>
<ul>
<li>Gathering all supported images from a specific source directory.</li>
<li>Iterating through our findings and bulk-upload them all in one go.</li>
</ul>
<p>So, let&rsquo;s do just that. But, first, let&rsquo;s set up some environmental variables so we can easily configure out script:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_COOKIE</span><span class="p">:=</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_TOKEN</span><span class="p">:=</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_DOMAIN</span><span class="p">:=</span><span class="si">}</span></span></span></code></pre></div>
</div>
<p>Slack only supports <code>png</code>, <code>gif</code> and <code>jpg</code> image formats, so lets see what we can find from our present working directory:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">files</span><span class="o">=</span><span class="k">$(</span>find . -iname <span class="se">\*</span>.gif -o -iname <span class="se">\*</span>.png -o -iname <span class="se">\*</span>.png -maxdepth 1<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[[</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">files</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;&#34;</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="nb">exit</span> <span class="m">0</span></span></span></code></pre></div>
</div>
<p>Exit if we can&rsquo;t find any results. No need to continue if we haven&rsquo;t a thing to upload.</p>
<p>Next, we just iterate through our findings using <code>read</code> and some more <a href="https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html">Bash parameter expansion</a> to determine what will be the new emoji&rsquo;s reference name:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">files</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> <span class="k">while</span> <span class="nb">read</span> -r path<span class="p">;</span> <span class="k">do</span> 
</span></span><span class="line"><span class="cl">  <span class="nv">name</span><span class="o">=</span><span class="k">$(</span>basename <span class="s2">&#34;</span><span class="si">${</span><span class="nv">path</span><span class="p">%.*</span><span class="si">}</span><span class="s2">&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span></span></span></code></pre></div>
</div>
<p>We can easily parse the response bodies with <code>jq</code> for some error checking and we&rsquo;re good to go:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> <span class="k">$(</span><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq -r <span class="s2">&#34;.ok&#34;</span><span class="k">)</span> <span class="o">==</span> <span class="nb">false</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq -r <span class="s2">&#34;.error&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span></span></span></code></pre></div>
</div>
<p>We now have all the information we need to upload each file in bulk. Put it all together and you&rsquo;ve got a working bulk emoji uploader:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">set</span> -eo pipefail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_COOKIE</span><span class="p">:=</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_TOKEN</span><span class="p">:=</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_DOMAIN</span><span class="p">:=</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">files</span><span class="o">=</span><span class="k">$(</span>find . -iname <span class="se">\*</span>.gif -o -iname <span class="se">\*</span>.png -o -iname <span class="se">\*</span>.png -maxdepth 1<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[[</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">files</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;&#34;</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">files</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> <span class="k">while</span> <span class="nb">read</span> -r path<span class="p">;</span> <span class="k">do</span> 
</span></span><span class="line"><span class="cl">  <span class="nv">name</span><span class="o">=</span><span class="k">$(</span>basename <span class="s2">&#34;</span><span class="si">${</span><span class="nv">path</span><span class="p">%.*</span><span class="si">}</span><span class="s2">&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nv">result</span><span class="o">=</span><span class="k">$(</span>
</span></span><span class="line"><span class="cl">    curl -s --compressed <span class="s2">&#34;https://</span><span class="si">${</span><span class="nv">SLACK_DOMAIN</span><span class="si">}</span><span class="s2">.slack.com/api/emoji.add&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">      -H <span class="s1">&#39;content-type: multipart/form-data&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">      -H <span class="s2">&#34;cookie: d=</span><span class="si">${</span><span class="nv">SLACK_COOKIE</span><span class="si">}</span><span class="s2">;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">      -F <span class="s2">&#34;token=</span><span class="si">${</span><span class="nv">SLACK_TOKEN</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">      -F <span class="s2">&#34;name=</span><span class="si">${</span><span class="nv">name</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">      -F <span class="nv">mode</span><span class="o">=</span>data <span class="se">\
</span></span></span><span class="line"><span class="cl">      -F <span class="s2">&#34;image=@</span><span class="si">${</span><span class="nv">path</span><span class="si">}</span><span class="s2">&#34;</span> 
</span></span><span class="line"><span class="cl">  <span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="k">$(</span><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq -r <span class="s2">&#34;.ok&#34;</span><span class="k">)</span> <span class="o">==</span> <span class="nb">false</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq -r <span class="s2">&#34;.error&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;uploaded :</span><span class="si">${</span><span class="nv">name</span><span class="si">}</span><span class="s2">:!&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span></span></span></code></pre></div>
</div>
<p>There you go! To test it out yourself, save this as an executable script and make sure your present working directory has some supported images in it. Pass through the your environmental variables when executing and you&rsquo;re off:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ <span class="nv">SLACK_DOMAIN</span><span class="o">=</span>*** <span class="nv">SLACK_COOKIE</span><span class="o">=</span>*** <span class="nv">SLACK_TOKEN</span><span class="o">=</span>*** ./upload.sh
</span></span><span class="line"><span class="cl">uploaded :stonks:!
</span></span><span class="line"><span class="cl">uploaded :boop:!
</span></span><span class="line"><span class="cl">uploaded :derp:!
</span></span><span class="line"><span class="cl">uploaded :booyah:!
</span></span><span class="line"><span class="cl">uploaded :merp-flakes:!</span></span></code></pre></div>
</div>
<p>Nice! 😊</p>
<h2 id="once-again-something-better">
  <a class="heading-link" href="#once-again-something-better">Once Again, Something Better!<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>While perfectly functional, there&rsquo;s not a lot of flexibility. No error checking, filtering or confirmation checks. If you&rsquo;re looking for something a bit more fleshed out, <a href="https://github.com/wilhelm-murdoch/slack-emoji-toolkit">look no further</a>!</p>
<h2 id="the-final-piece-">
  <a class="heading-link" href="#the-final-piece-">The Final Piece &hellip;<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>So far, we&rsquo;ve gone over how to download and upload large sets of emoji, but what if you want to nuke them from orbit? The final article in this series will cover how to bulk delete while using advanced filters to pin-point specific sets of emoji you wish to remove.</p>
<p>Hope you&rsquo;ve learned something useful!</p>]]></content:encoded></item><item><title>Liberating Custom Slack Emojis</title><link>https://wilhelm.codes/blog/liberating-custom-slack-emojis/</link><pubDate>Tue, 20 Sep 2022 00:00:00 +0000</pubDate><author>0xdeadbeef@devilmayco.de (Wilhelm Murdoch)</author><guid>https://wilhelm.codes/blog/liberating-custom-slack-emojis/</guid><category>emoji</category><category>api</category><category>bash</category><wc:kind>post</wc:kind><description>As is stupid tradition, whenever I start at a new company, one of the first things I like to do while getting settled in is upload my favourite emojis to whatever real-time messaging platform is in use. I know it&amp;rsquo;s childish, but silly memes and emojis are great ways for you to break the ice with your new coworkers.</description><content:encoded><![CDATA[<p>As is stupid tradition, whenever I start at a new company, one of the first things I like to do while getting settled in is upload my favourite emojis to whatever real-time messaging platform is in use. I know it&rsquo;s childish, but silly memes and emojis are great ways for you to break the ice with your new coworkers.</p>
<p>But, when it comes time to part ways it&rsquo;s only understandable to want to gather your stuff for the next place to start the process again. Over the years one can collect and curate quite a nice hoard of stupid images.</p>
<p>I&rsquo;ve been using Slack prolifically for the past few years. Unfortunately, there is no simple way of exporting your precious collection outside of the old “right-click and save” method. This is effective for smaller collections of a couple dozen or so, but becomes impractical when there are hundreds or even thousands.</p>
<p>And, of course, I&rsquo;m not alone. There have been a non-zero number of times when someone left the company only to hit me up later asking for an emoji archive.</p>
<p>Yes, this really happens and something <em>must</em> be done about it!</p>
<p>What follows is an unnecessarily long dive into a little bit of reverse engineering one of Slack&rsquo;s API endpoints, subverting it for our own use and writing a stupid little tool to help ourselves ( and others ) automate this process for the future.</p>
<h2 id="some-investigative-work">
  <a class="heading-link" href="#some-investigative-work">Some Investigative Work<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>There just has to be a straightforward way we can <em>mostly</em> automate this. So, I did some cursory digging around the Chrome Developer Tools console and found this endpoint in the “Network” tab:</p>
<div class="code-block">
  <pre tabindex="0"><code>https://edgeapi.slack.com/cache/T0XXXX/emojis/list?fp=97</code></pre>
</div>
<p>The <code>T0XXXX</code> will be your workspace, or team, id. Make note of it, you&rsquo;ll need it later.</p>
<p>If you intercept one of these requests and take a peak at the “Response” tab in the “Network” panel, you&rsquo;ll see a JSON response with a structure similar to:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">JSON</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;ok&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;next_marker&#34;</span><span class="p">:</span> <span class="s2">&#34;a-custom-emoji&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;results&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">		<span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;another-custom-emoji&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">			<span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;https://emoji.slack-edge.com/T0XXXX/another-custom-emoji/xxxxxxx.png&#34;</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">		<span class="err">...</span> <span class="err">more</span> <span class="err">emojis</span> <span class="err">...</span>
</span></span><span class="line"><span class="cl">	<span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
</div>
<ul>
<li><code>ok</code> tells you whether the request was successful or not. If it returns <code>false</code> there will be an additional field named <code>error</code> that should clue you into what went wrong.</li>
<li><code>next_marker</code> tells you where in the list of custom emoji the <em>next</em> page of results should start. We use this as the value of <code>marker</code> in subsequent request payloads ( see below ). This is effectively how you page through large lists of emoji.</li>
<li><code>results</code> should be obvious, but it contains an array of objects with <code>name</code> and <code>value</code> fields.
<ul>
<li><code>name</code> is the alias of the emoji.</li>
<li><code>value</code> is the direct CDN URL to the emoji.</li>
</ul>
</li>
</ul>
<p>You may see other fields, but <code>next_marker</code> and <code>value</code> are the only fields we really care about.</p>
<p>Next, If you take a look at the “Payload” tab in the “Network” panel for the same request, you&rsquo;ll see something similar to:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">JSON</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;token&#34;</span><span class="p">:</span> <span class="s2">&#34;xoxc-xxxx-xxxx-xxxx&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;count&#34;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	<span class="nt">&#34;marker&#34;</span><span class="p">:</span> <span class="s2">&#34;some-other-emoji&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
</div>
<ul>
<li><code>token</code> is the API request token the Slack client uses to make the emoji list request on your behalf.</li>
<li><code>count</code> is the amount of emojis to return as a page.</li>
<li><code>marker</code> tells the API where in the emoji list to start paging.</li>
</ul>
<p>The final piece of the puzzle is getting the value of your session cookie. You can grab this by clicking the “Request Headers” section under the “Headers” tab. You should see a header labeled <code>cookie</code>.</p>
<p>There&rsquo;s going to be a fairly large block of text to parse through, but you&rsquo;re looking for the cookie named <code>d</code>. Save everything between the <code>d=</code> and the closing <code>;</code>. You&rsquo;re going to need this to authenticate with the Slack API itself.</p>
<h2 id="testing-your-findings">
  <a class="heading-link" href="#testing-your-findings">Testing Your Findings<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>We now understand the structure of our payload. We also have the endpoint to hit as well as our tokens and session cookie to authenticate. Piecing it all together, you can now use cURL to hit the API directly from your terminal:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ curl --silent https://edgeapi.slack.com/cache/&lt;team&gt;/emojis/list?fp<span class="o">=</span><span class="m">97</span>
</span></span><span class="line"><span class="cl">  -H <span class="s1">&#39;cookie: d=&lt;cookie&gt;;&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --data-raw <span class="s1">&#39;{&#34;token&#34;:&#34;&lt;token&gt;&#34;,&#34;count&#34;:10}&#39;</span></span></span></code></pre></div>
</div>
<p>Replace the following:</p>
<ul>
<li><code>&lt;team&gt;</code> is your workspace, or team, id.</li>
<li><code>&lt;cookie&gt;</code> is your session cookie value.</li>
<li><code>&lt;token&gt;</code> is your request token.</li>
</ul>
<p>If everything worked out, you should have successful API response like the one above. If the response has root field named <code>marker_next</code>, make note of the associated value to help page through subsequent results. Keep doing this in a loop until your response no longer returns a <code>marker_next</code> field.</p>
<p>Once that happens, you&rsquo;re at the end of the list.</p>
<p>With a little bit of help from <code>jq</code> to parse and filter the JSON responses, we can grab a simple list of URLs.</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ &lt;previous_curl_command&gt; <span class="p">|</span> jq -r <span class="s1">&#39;.results[].value&#39;</span>
</span></span><span class="line"><span class="cl">https://emoji.slack-edge.com/T0XXXX/no/060fca9aa2581a93.png
</span></span><span class="line"><span class="cl">https://emoji.slack-edge.com/T0XXXX/nods/c7fe54342ab5b8b6.gif
</span></span><span class="line"><span class="cl">https://emoji.slack-edge.com/T0XXXX/nods-back/fbd1b5384cdd0905.gif
</span></span><span class="line"><span class="cl">https://emoji.slack-edge.com/T0XXXX/nomnomnom/612cfc74c785010d.gif
</span></span><span class="line"><span class="cl">https://emoji.slack-edge.com/T0XXXX/octocat/627964d7c9.png
</span></span><span class="line"><span class="cl">https://emoji.slack-edge.com/T0XXXX/ohyou/6a352df984d9c076.gif
</span></span><span class="line"><span class="cl">https://emoji.slack-edge.com/T0XXXX/one-sec-cooking/e749627cb088859d.png
</span></span><span class="line"><span class="cl">https://emoji.slack-edge.com/T0XXXX/oof/db94710445cbb206.png
</span></span><span class="line"><span class="cl">https://emoji.slack-edge.com/T0XXXX/petrol/35ed3db238f795fc.png
</span></span><span class="line"><span class="cl">https://emoji.slack-edge.com/T0XXXX/piggy/b7762ee8cd.png</span></span></code></pre></div>
</div>
<p>We now understand how to collect everything we need, so let&rsquo;s automate this a bit more.</p>
<h2 id="the-implementation">
  <a class="heading-link" href="#the-implementation">The Implementation<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>We&rsquo;re going to write a little script that&rsquo;ll download all the custom emojis from the target Slack workspace. It&rsquo;s going to perform the following steps:</p>
<ol>
<li>Define a starting JSON payload.</li>
<li>Create a run loop.</li>
<li>With each loop we make a request using the API endpoint.</li>
<li>Do some error checking.</li>
<li>Iterate through the results and save them to disk.</li>
<li>If the results contain a <code>next_marker</code> update the JSON payload with a <code>marker</code> field and continue the run loop.</li>
<li>Keep going until the last request no longer provides a <code>next_marker</code>.</li>
</ol>
<p>First, we want to define some environmental variables so we can easily configure the script:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_WORKSPACE_ID</span><span class="p">:=</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_COOKIE</span><span class="p">:=</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_TOKEN</span><span class="p">:=</span><span class="si">}</span></span></span></code></pre></div>
</div>
<p>Now we define our JSON payload object. We use <code>jq</code> here to keep things easy. Not only is it great for parsing and filtering JSON in Bash, it also makes building and manipulating objects simple:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">payload</span><span class="o">=</span><span class="k">$(</span>
</span></span><span class="line"><span class="cl">  jq -nc <span class="se">\
</span></span></span><span class="line"><span class="cl">    --arg     token <span class="s2">&#34;</span><span class="si">${</span><span class="nv">SLACK_TOKEN</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    --argjson count <span class="s2">&#34;100&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="s1">&#39;{
</span></span></span><span class="line"><span class="cl"><span class="s1">      token: $token, 
</span></span></span><span class="line"><span class="cl"><span class="s1">      count: $count
</span></span></span><span class="line"><span class="cl"><span class="s1">    }&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">)</span></span></span></code></pre></div>
</div>
<p>Next, is the run loop where we make our API requests. This passes through the payload object we just defined while also targeting our workspace:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">while</span> true<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="nv">result</span><span class="o">=</span><span class="k">$(</span>
</span></span><span class="line"><span class="cl">    curl -s --compressed <span class="s2">&#34;https://edgeapi.slack.com/cache/</span><span class="si">${</span><span class="nv">SLACK_WORKSPACE_ID</span><span class="si">}</span><span class="s2">/emojis/list?fp=97&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">      -H <span class="s2">&#34;cookie: d=</span><span class="si">${</span><span class="nv">SLACK_COOKIE</span><span class="si">}</span><span class="s2">;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">      --data-raw <span class="s2">&#34;</span><span class="si">${</span><span class="nv">payload</span><span class="si">}</span><span class="s2">&#34;</span> 
</span></span><span class="line"><span class="cl">  <span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span></span></span></code></pre></div>
</div>
<p>We obviously want to do some simple error checking so we know what we did wrong if the requests fail. Grab the error, spit it out and terminate the script.</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> <span class="k">$(</span><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq -r <span class="s2">&#34;.ok&#34;</span><span class="k">)</span> <span class="o">==</span> <span class="nb">false</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq -r <span class="s2">&#34;.error&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span></span></span></code></pre></div>
</div>
<p>We now start iterating through any of the results we got from the initial request. We really only care about the <code>value</code> field as the URL gives us all the pieces we need to give our emojis a human-readable filename.</p>
<p>Here we treat the resulting URL as a standard file path and use <code>dirname</code> paired with <code>basename</code> to get the name of the parent directory of the file. The parent directory holds the actual name of the emoji, while the file itself is only a random hash. We use <a href="https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html">Bash&rsquo;s parameter expansion</a> feature to construct our filename.</p>
<p>Finally, we download the remote file to the current directory:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span>  <span class="p">|</span> jq -r <span class="s1">&#39;.results[].value&#39;</span> <span class="p">|</span> <span class="k">while</span> <span class="nb">read</span> -r url<span class="p">;</span> <span class="k">do</span> 
</span></span><span class="line"><span class="cl"><span class="nv">name</span><span class="o">=</span><span class="k">$(</span>basename <span class="k">$(</span>dirname <span class="s2">&#34;</span><span class="si">${</span><span class="nv">url</span><span class="si">}</span><span class="s2">&#34;</span><span class="k">))</span>
</span></span><span class="line"><span class="cl">curl -s -o <span class="s2">&#34;</span><span class="si">${</span><span class="nv">name</span><span class="si">}</span><span class="s2">.</span><span class="si">${</span><span class="nv">url</span><span class="p">##*.</span><span class="si">}</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">url</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span></span></span></code></pre></div>
</div>
<p>We need to check if there are more results to page through, so let&rsquo;s look for the <code>marker_next</code> field. If we can&rsquo;t find one, then mission accomplished; all done. However, if we do get a value, we take the previous payload we constructed and <em>add</em> the <code>marker</code> field.</p>
<p>The next loop will pick this change up and pass it on through to the API request telling Slack to give us the next page of precious emojis.</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">marker</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq -r <span class="s2">&#34;.next_marker&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="o">[[</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">marker</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;null&#34;</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">payload</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">payload</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq --arg marker <span class="s2">&#34;</span><span class="si">${</span><span class="nv">marker</span><span class="si">}</span><span class="s2">&#34;</span> <span class="s1">&#39;. + { marker: $marker }&#39;</span><span class="k">)</span></span></span></code></pre></div>
</div>
<h2 id="the-finished-product">
  <a class="heading-link" href="#the-finished-product">The Finished Product<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">set</span> -eo pipefail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_WORKSPACE_ID</span><span class="p">:=</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_COOKIE</span><span class="p">:=</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">: <span class="si">${</span><span class="nv">SLACK_TOKEN</span><span class="p">:=</span><span class="si">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">payload</span><span class="o">=</span><span class="k">$(</span>
</span></span><span class="line"><span class="cl">  jq -nc <span class="se">\
</span></span></span><span class="line"><span class="cl">    --arg     token <span class="s2">&#34;</span><span class="si">${</span><span class="nv">SLACK_TOKEN</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    --argjson count <span class="s2">&#34;100&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="s1">&#39;{
</span></span></span><span class="line"><span class="cl"><span class="s1">      token: $token, 
</span></span></span><span class="line"><span class="cl"><span class="s1">      count: $count
</span></span></span><span class="line"><span class="cl"><span class="s1">    }&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">while</span> true<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="nv">result</span><span class="o">=</span><span class="k">$(</span>
</span></span><span class="line"><span class="cl">    curl -s --compressed <span class="s2">&#34;https://edgeapi.slack.com/cache/</span><span class="si">${</span><span class="nv">SLACK_WORKSPACE_ID</span><span class="si">}</span><span class="s2">/emojis/list?fp=97&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">      -H <span class="s2">&#34;cookie: d=</span><span class="si">${</span><span class="nv">SLACK_COOKIE</span><span class="si">}</span><span class="s2">;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">      --data-raw <span class="s2">&#34;</span><span class="si">${</span><span class="nv">payload</span><span class="si">}</span><span class="s2">&#34;</span> 
</span></span><span class="line"><span class="cl">  <span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="k">$(</span><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq -r <span class="s2">&#34;.ok&#34;</span><span class="k">)</span> <span class="o">==</span> <span class="nb">false</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq -r <span class="s2">&#34;.error&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span>  <span class="p">|</span> jq -r <span class="s1">&#39;.results[].value&#39;</span> <span class="p">|</span> <span class="k">while</span> <span class="nb">read</span> -r url<span class="p">;</span> <span class="k">do</span> 
</span></span><span class="line"><span class="cl">    <span class="nv">name</span><span class="o">=</span><span class="k">$(</span>basename <span class="k">$(</span>dirname <span class="s2">&#34;</span><span class="si">${</span><span class="nv">url</span><span class="si">}</span><span class="s2">&#34;</span><span class="k">))</span>
</span></span><span class="line"><span class="cl">    curl -s -o <span class="s2">&#34;</span><span class="si">${</span><span class="nv">name</span><span class="si">}</span><span class="s2">.</span><span class="si">${</span><span class="nv">url</span><span class="p">##*.</span><span class="si">}</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">url</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="k">done</span> 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nv">marker</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">result</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq -r <span class="s2">&#34;.next_marker&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">  <span class="o">[[</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">marker</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">==</span> <span class="s2">&#34;null&#34;</span> <span class="o">]]</span> <span class="o">&amp;&amp;</span> <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nv">payload</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">payload</span><span class="si">}</span><span class="s2">&#34;</span> <span class="p">|</span> jq --arg marker <span class="s2">&#34;</span><span class="si">${</span><span class="nv">marker</span><span class="si">}</span><span class="s2">&#34;</span> <span class="s1">&#39;. + { marker: $marker }&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span></span></span></code></pre></div>
</div>
<p>This is pretty much it. A barebones script that downloads all findings to its present working directory. To test it out yourself, make the script executable, define the environmental variables and fire it off like so:</p>
<div class="code-block">
  <div class="code-head">
    <span class="code-file"></span>
    <span class="code-lang">Bash</span>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ <span class="nv">SLACK_WORKSPACE_ID</span><span class="o">=</span>*** <span class="nv">SLACK_COOKIE</span><span class="o">=</span>*** <span class="nv">SLACK_TOKEN</span><span class="o">=</span>*** ./fetch.sh</span></span></code></pre></div>
</div>
<h2 id="an-even-better-version">
  <a class="heading-link" href="#an-even-better-version">An Even Better Version!<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>I know I just made you read through all this, but you also could&rsquo;ve just <a href="https://github.com/wilhelm-murdoch/slack-emoji-toolkit">downloaded the tool from here</a>. It&rsquo;s got heaps more bells and whistles ( if you&rsquo;re into that sort of thing):</p>
<h2 id="the-bitter-irony">
  <a class="heading-link" href="#the-bitter-irony">The Bitter Irony<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p>In typical bored engineer fashion, I&rsquo;ve spent more time writing this article, documenting and publishing the code than I ever would have personally just “handraulically” doing this from time to time.</p>
<p>Hopefully, you&rsquo;ve learned something new! 🤞</p>
<h2 id="bonus-relevant-xkcd-image">
  <a class="heading-link" href="#bonus-relevant-xkcd-image">Bonus Relevant XKCD Image<span class="heading-anchor" aria-hidden="true">#</span>
  </a>
</h2>
<p><img
  src="https://wilhelm.codes/blog/liberating-custom-slack-emojis/image-1_hu_190150482d40845e.webp"
  srcset="/blog/liberating-custom-slack-emojis/image-1_hu_190150482d40845e.webp 736w"
  sizes="(max-width: 48rem) 100vw, 736px"
  width="736"
  height="740"
  alt=""
  
  loading="lazy"
  decoding="async"
/>

source: <a href="https://xkcd.com/1319/">xkcd: automation</a></p>]]></content:encoded></item></channel></rss>