<?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 · Slack</title><link>https://wilhelm.codes/tags/slack/</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/slack/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></channel></rss>