<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tom Quirk</title>
    <description>The latest articles on DEV Community by Tom Quirk (@tomquirk).</description>
    <link>https://dev.to/tomquirk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F366945%2F18e5a22d-96d1-4529-9eac-aebf8d77a634.jpeg</url>
      <title>DEV Community: Tom Quirk</title>
      <link>https://dev.to/tomquirk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tomquirk"/>
    <language>en</language>
    <item>
      <title>Create a Slack Bot with Node.js: The Definitive Guide</title>
      <dc:creator>Tom Quirk</dc:creator>
      <pubDate>Sat, 09 Oct 2021 06:10:17 +0000</pubDate>
      <link>https://dev.to/tomquirk/create-a-slack-bot-with-node-js-gkp</link>
      <guid>https://dev.to/tomquirk/create-a-slack-bot-with-node-js-gkp</guid>
      <description>&lt;p&gt;If this is the millionth article you've found on how to build a Slack bot with Node.js, I'm sorry. &lt;/p&gt;

&lt;p&gt;I've built a few Slack bots, some of which have launched on the Slack App Directory. I'm going to give you a no-bullshit tutorial on creating a Slack bot with Node.js.&lt;/p&gt;

&lt;p&gt;We'll build an internal tool that your team can use to get information about your customers using a slash command. I'm not going to teach you how to build a Slack app for the Slack App Directory.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quick plug: if you want to skip this entire article and focus on writing your internal tool, try &lt;a href="https://dev.toRuntime"&gt;Runtime&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Set up Ngrok
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ngrok.com/"&gt;Ngrok&lt;/a&gt; is how you'll connect your Slack workspace (on the internet) to your locally-running Slack bot (on your machine). It has a free plan, but the Basic plan is affordable and extremely useful for building Slack bots.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Security note&lt;/strong&gt;: Ngrok exposes your machine to the internet, making it a potential attack vector. If you're building your Slack bot on your work machine, get Ngrok approved by your security team.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create an Ngrok account and follow the set-up steps. When you're finished, expose a web server on port 8080:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should produce the Ngrok console UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok by @inconshreveable

Tunnel Status                 online
Version                       2.0/2.0
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://92832de0.ngrok.io -&amp;gt; localhost:8080
Forwarding                    https://92832de0.ngrok.io -&amp;gt; localhost:8080

Connnections                  ttl     opn     rt1     rt5     p50     p90
                              0       0       0.00    0.00    0.00    0.00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you're on the free Ngrok plan, &lt;strong&gt;don't close this terminal window&lt;/strong&gt;. The forwarding URL is randomly generated, and will change when you restart Ngrok.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Take note of the &lt;code&gt;Forwarding&lt;/code&gt; URL. In the example above, it is: &lt;code&gt;https://92832de0.ngrok.io&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up your Slack app
&lt;/h2&gt;

&lt;p&gt;Before we start writing code, we need to create a Slack App.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;a href="https://api.slack.com/apps?new_app=1"&gt;https://api.slack.com/apps?new_app=1&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;From an app manifest&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;&amp;lt;NGROK_FORWARDING_URL&amp;gt;&lt;/code&gt; with your Ngrok forwarding URL in the App Manifest YAML file below.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;In the Ngrok example above, we would use &lt;code&gt;https://92832de0.ngrok.io&lt;/code&gt;. So, the &lt;code&gt;redirect_urls&lt;/code&gt; example below would become: &lt;code&gt;https://92832de0.ngrok.io/slack/oauth_redirect&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt; Copy-paste the YAML contents in the &lt;strong&gt;Enter app manifest below&lt;/strong&gt; section.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;_metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;major_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;minor_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;display_information&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodeBot&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Our internal Slack bot.&lt;/span&gt;
&lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;bot_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;display_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodeBot&lt;/span&gt;
    &lt;span class="na"&gt;always_online&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;slash_commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/customer&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;NGROK_FORWARDING_URL&amp;gt;/slack/events&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get data about a customer&lt;/span&gt;
      &lt;span class="na"&gt;usage_hint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/customer &amp;lt;customer id&amp;gt;&lt;/span&gt;
      &lt;span class="na"&gt;should_escape&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="na"&gt;oauth_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redirect_urls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;NGROK_FORWARDING_URL&amp;gt;/slack/oauth_redirect&lt;/span&gt;
  &lt;span class="na"&gt;scopes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;bot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;commands&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;chat:write&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;chat:write.public&lt;/span&gt;
&lt;span class="na"&gt;settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;org_deploy_enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;socket_mode_enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;token_rotation_enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;App Manifest for your Slack bot&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select &lt;strong&gt;Next&lt;/strong&gt;, then select &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Scroll down to the **App credentials **section and take note of the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Client ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Client secret&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Signing secret&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, install the app to your Slack workspace.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select &lt;strong&gt;Install App&lt;/strong&gt; on the left sidebar.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Install to Workspace&lt;/strong&gt;, and select &lt;strong&gt;Allow&lt;/strong&gt; on the installation page.&lt;/li&gt;
&lt;li&gt;Take note of the &lt;code&gt;Bot User OAuth Token&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Set up your Slack bot code
&lt;/h2&gt;

&lt;p&gt;Let's ensure your local environment is set up correctly. The dependencies for this Slack bot are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;node &amp;gt;=12.13.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm &amp;gt;=6.12.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start by creating a new directory for the Slack bot and initialising &lt;code&gt;npm&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;slackbot-node
&lt;span class="nb"&gt;cd &lt;/span&gt;slackbot-node
npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the prompts (tip: hold down the Enter key).&lt;/p&gt;

&lt;p&gt;Let's install the project dependencies. Our main dependency is &lt;a href="https://github.com/slackapi/bolt-js"&gt;Bolt&lt;/a&gt;, the official Slack framework for building Slack apps with JavaScript.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save @slack/bolt dotenv
npm install --save-dev nodemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Add your environment variables
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;.env&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Add the following contents to your &lt;code&gt;.env&lt;/code&gt; file, and add the values you took note of in the &lt;strong&gt;Set up your Slack app&lt;/strong&gt; section.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SLACK_CLIENT_ID=&amp;lt;YOUR SLACK CLIENT ID&amp;gt;
SLACK_CLIENT_SECRET=&amp;lt;YOUR SLACK CLIENT SECRET&amp;gt;
SLACK_SIGNING_SECRET=&amp;lt;YOUR SLACK SIGNING SECRET&amp;gt;
SLACK_BOT_USER_TOKEN=&amp;lt;YOUR SLACKBOT USER TOKEN&amp;gt;
SLACK_OAUTH_STATE_SECRET='my-state-secret'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the bot server
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create an &lt;code&gt;index.js&lt;/code&gt; file. This will be the entry point for our bot server.&lt;/li&gt;
&lt;li&gt;Copy-paste the following code:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@slack/bolt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SLACK_BOT_USER_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;signingSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SLACK_SIGNING_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Slash command handler goes here.&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`🤖 Slack bot at your service (http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt; Check the server works by starting the bot server with &lt;code&gt;nodemon&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;nodemon&lt;/code&gt; restarts the server whenever we edit our &lt;code&gt;index.js&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nodemon &lt;span class="nt"&gt;--exec&lt;/span&gt; node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[nodemon] 2.0.13
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node index.js`
🤖 Slack bot at your service (http://localhost:8080)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add a slash command
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;App Manifest&lt;/strong&gt; file that we used to create our Slack app has already added a slash command for us; it's called &lt;code&gt;/customer&lt;/code&gt;. Whenever somebody in your Slack workspace types &lt;code&gt;/customer&lt;/code&gt;, Slack will send a POST request to your bot server; we need to program our bot server to respond correctly to the &lt;code&gt;/customer&lt;/code&gt; slash command.&lt;/p&gt;

&lt;p&gt;Add the following code to &lt;code&gt;index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Handle the /customer slash command
app.command('/customer', async ({ command, ack, respond }) =&amp;gt; {
  // Acknowledge command request
  await ack();

  await respond('Slack bot at your service!');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can test the slash command in Slack. Type &lt;code&gt;/customer&lt;/code&gt; in Slack. This should yield the following result:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NKjGEoSi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.runtimehq.com/content/images/2021/10/Screen-Shot-2021-10-09-at-3.21.55-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NKjGEoSi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.runtimehq.com/content/images/2021/10/Screen-Shot-2021-10-09-at-3.21.55-pm.png" alt="Slash command example"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Getting customer data
&lt;/h3&gt;

&lt;p&gt;Finally, the juicy part. The method for getting customer data will vary based on your tech stack and where your customer data lives. Typically, you'll execute a database query here. For now, let's return some dummy user data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Format your response with the &lt;a href="https://app.slack.com/block-kit-builder/T019RHBDBKP#%7B%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Jane%20Doe*%20(jane@company.com)%5Cn%3E-%20Enterprise%20plan%5Cn%3E-%2010%20active%20users%5Cn%3E-%20%241002%20total%20spend%22%7D%7D%5D%7D"&gt;Slack Block Kit Builder&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customerData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jane Doe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jane@company.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;activeUsers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enterprise&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;totalSpend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$1002.26&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Format the text however you like; Slack supports Markdown.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`*&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;customerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;* (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;customerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;customerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; plan\n&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;customerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeUsers&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; users\n&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;customerData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalSpend&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; total spend`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;response_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;in_channel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// make the response public&lt;/span&gt;
  &lt;span class="na"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;section&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mrkdwn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's run it in Slack and see the result. Type &lt;code&gt;/customer&lt;/code&gt; in Slack:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oe2Qaz1_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.runtimehq.com/content/images/2021/10/Screen-Shot-2021-10-09-at-3.21.38-pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oe2Qaz1_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.runtimehq.com/content/images/2021/10/Screen-Shot-2021-10-09-at-3.21.38-pm.png" alt="Final slash command example"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Building and maintaining internal Slack bots is a hassle. I built &lt;a href="https://runtimehq.com"&gt;Runtime&lt;/a&gt; to help you rapidly create custom Slack bots. Write your internal script in your technology of choice, and Runtime handles the rest. Let me know what you think on &lt;a href="https://twitter.com/tom__quirk"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>tutorial</category>
      <category>slackbot</category>
    </item>
    <item>
      <title>5 reasons why Slack will reject your Slack app</title>
      <dc:creator>Tom Quirk</dc:creator>
      <pubDate>Fri, 09 Oct 2020 23:38:02 +0000</pubDate>
      <link>https://dev.to/tomquirk/5-reasons-why-slack-will-reject-your-slack-app-39m8</link>
      <guid>https://dev.to/tomquirk/5-reasons-why-slack-will-reject-your-slack-app-39m8</guid>
      <description>&lt;p&gt;At a time when businesses are increasingly relying on Slack for day-to-day operations, the Slack App Directory is thriving. These days, there are Slack apps for everything: from &lt;a href="https://slack.com/apps/A04E6JX41-polly"&gt;polls&lt;/a&gt; to &lt;a href="https://slack.com/apps/A0J4UNFLN-heytaco"&gt;team recognition&lt;/a&gt;, to &lt;a href="https://slack.com/apps/A355V71K7-standupiy"&gt;daily standups&lt;/a&gt;, to &lt;a href="https://slack.com/apps/A01A18SMWCC-allybot"&gt;promoting inclusive language&lt;/a&gt;. There has never been a better time to be a Slack user.&lt;/p&gt;

&lt;p&gt;Likewise, there has never been a better time to create a Slack app!&lt;/p&gt;

&lt;p&gt;But it's not all sunshine and roses. I've been through the Slack app process twice, first with &lt;a href="https://slack.com/apps/ARPAT12GY-uppit"&gt;UPPIT&lt;/a&gt; and now with &lt;a href="https://slack.com/apps/A01A18SMWCC-allybot"&gt;AllyBot&lt;/a&gt;. Building a Slack app is fun and Slack is always improving the &lt;a href="(https://twitter.com/slackapi/status/1121107098895392768)"&gt;developer experience&lt;/a&gt;. But, there are a few gotchas that have cost me a lot of time during the review process.&lt;/p&gt;

&lt;p&gt;Here are 5 reasons why Slack will reject your bot, and what you can do about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Security
&lt;/h2&gt;

&lt;p&gt;Do yourself a favor and use &lt;a href="https://github.com/boltpkg/bolt"&gt;bolt.js&lt;/a&gt;. This one might be obvious, but it certainly wasn't to me when I built &lt;a href="https://uppit.io/"&gt;UPPIT&lt;/a&gt;. Don't get me wrong, you can build a basic Slack app with the Slack web API and your backend framework of choice; this is what I did with UPPIT.&lt;/p&gt;

&lt;p&gt;But when you go to submit your app to the Slack App Directory, you will soon realize there are a bunch of security things you probably didn't consider. OAuth and token management, and &lt;a href="https://api.slack.com/authentication/verifying-requests-from-slack"&gt;verifying requests from Slack&lt;/a&gt; and the ones I've failed with in the past. Both these topics deserve an article, but the takeaway is that bolt.js handles a lot of this stuff for you. It provides sensible interfaces and callbacks for the things you need to customize. It also provides simple wrappers for the Slack web API out of the box. bolt.js an obvious choice that allows you to focus on building your app.&lt;/p&gt;

&lt;p&gt;I estimate 20% of the time I spent on UPPIT was on these auxiliary tasks, whereas when I used bolt.js for &lt;a href="https://allybot.io/"&gt;AllyBot&lt;/a&gt;, it was more like 5%.&lt;/p&gt;

&lt;p&gt;Don't reinvent the wheel - use bolt.js!&lt;/p&gt;

&lt;h2&gt;
  
  
  2. No customer support or poor branding
&lt;/h2&gt;

&lt;p&gt;When you're in the trenches building your app, it's easy to forget that you need to market the thing! Slack requires a bunch of items on this front. Let's go through some of the ways you can succeed on this front.&lt;/p&gt;

&lt;h3&gt;
  
  
  Privacy policy and Terms of Service
&lt;/h3&gt;

&lt;p&gt;Slack will not accept your app without a link to a Privacy Policy and a Terms of Service. It's best to seek legal advice, but you can also get pretty far by using existing Slack apps as inspiration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Support email address and contact form
&lt;/h3&gt;

&lt;p&gt;You need an email address for support and a support page on your website. For email, I use &lt;a href="https://www.zoho.com/"&gt;Zoho&lt;/a&gt;. It's free, and I've set up &lt;a href="mailto:tom@allybot.io"&gt;tom@allybot.io&lt;/a&gt;, and a "catch-all" alias that points to &lt;a href="mailto:tom@allybot.io"&gt;tom@allybot.io&lt;/a&gt;. This means I can list my app's support contact as something like &lt;a href="mailto:support@allybot.io"&gt;support@allybot.io&lt;/a&gt; and I'll receive any emails sent there at &lt;a href="mailto:tom@allybot.io"&gt;tom@allybot.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the support page, a simple contact form does the job - check out &lt;a href="https://allybot.io/#support"&gt;https://allybot.io/#support&lt;/a&gt; for an example (built with &lt;a href="//carrd.co"&gt;https://carrd.co&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Add to Slack button
&lt;/h3&gt;

&lt;p&gt;Make sure your website has an Add to Slack button. If you're using bolt.js, link this button to https:///slack/install. Redirect the user to an "Install Successful" page once they've successfully installed your app (Slack requires this).&lt;/p&gt;

&lt;h3&gt;
  
  
  Branding
&lt;/h3&gt;

&lt;p&gt;Slack has a bunch of &lt;a href="https://slack.dev/guides/AppUIGuidelines.pdf"&gt;resources&lt;/a&gt; on this topic, but the bottom line is this: keep your branding consistent and don't conflict with Slack's branding. Simple!&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Bad use of the App Home
&lt;/h2&gt;

&lt;p&gt;Utilizing the App Home in Slack can be confusing, but this is a must-do. Slack has a &lt;a href="https://api.slack.com/surfaces/tabs/events"&gt;whole article&lt;/a&gt; on this, but here are some essentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Send the user who installed your app an intro message
&lt;/h3&gt;

&lt;p&gt;When a user installs your app, you need to send them a message that explains to them how to get started. Here is &lt;a href="https://allybot.io/"&gt;AllyBot&lt;/a&gt;'s welcome message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nYhF-Vcw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tallordertech.co/p/5-reasons-why-slack-will-reject-your-slack-app/allybot-installed-welcome-message.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nYhF-Vcw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tallordertech.co/p/5-reasons-why-slack-will-reject-your-slack-app/allybot-installed-welcome-message.png" alt="AllyBot's installed welcome message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, make sure you only send this once.&lt;/p&gt;

&lt;h3&gt;
  
  
  Respond to app_home_opened event
&lt;/h3&gt;

&lt;p&gt;When a user other than the user that installed your app opens your apps App Home tab, you need to greet them as well! Again, this should only be a once-off. I have a flag for app_home_opened in the user table of the database, set to false. When the user first opens the App Home tab, this flag flips to true so I know never to send the message to the user again. The message can be like the first one, for example, this is how &lt;a href="https://allybot.io/"&gt;AllyBot.io&lt;/a&gt; responds to app_home_opened:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OKz_b8aI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tallordertech.co/p/5-reasons-why-slack-will-reject-your-slack-app/allybot-user-welcome-message.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OKz_b8aI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://tallordertech.co/p/5-reasons-why-slack-will-reject-your-slack-app/allybot-user-welcome-message.png" alt="AllyBot’s user welcome message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that you will need to request the &lt;code&gt;im:write&lt;/code&gt; scope to start chats with users.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Not giving good reasons for your requested OAuth scopes
&lt;/h2&gt;

&lt;p&gt;This is a common reason Slack apps get held up in the review process. You should take the time to consider if your app needs all the scopes you are requesting. Slack will reject anything that seems like a "nice to have".&lt;/p&gt;

&lt;p&gt;Give genuine reasons about why you need a certain scope. And be transparent. If you are capturing user emails (with the &lt;code&gt;users:read.email&lt;/code&gt; scope), then say so.&lt;/p&gt;

&lt;p&gt;The same applies to your data retention policies. Don't simply say "We will retain your data as per our privacy policy". Give a short, clear and concise description about how you plan to handle your user's data. Something like "When you ask us to delete your data, we will respond to you via email by 24 hours. We will aim to fulfill your data removal request with 24 hours of responding, and will inform you when your data has been removed. For more information, see our Privacy Policy".&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Poor error handling
&lt;/h2&gt;

&lt;p&gt;During the review process, Slack will try and break your app. But don't see this as a negative; we should be super grateful to get free QA testing (thanks Slack)! Using bolt.js will help you here. But, if your Slack app uses slash commands, or "actions" (buttons, etc.), think carefully about where your app could fail.&lt;/p&gt;

&lt;p&gt;Make sure to send the user a message when errors occur. An &lt;a href="https://api.slack.com/methods/chat.postEphemeral"&gt;"ephemeral"&lt;/a&gt; message is a good way to do this; an inline, private message to the user interacting with your app. Something as simple as "Oops, something went wrong 😢" is a good start, but always try to &lt;a href="https://uxplanet.org/how-to-write-good-error-messages-858e4551cd4"&gt;give direction to the user&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask me anything
&lt;/h2&gt;

&lt;p&gt;Have I missed something? Need something clarified? Hit me up on Twitter &lt;a href="https://twitter.com/tom__quirk"&gt;@tom__quirk&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>slack</category>
      <category>slackbot</category>
      <category>slackapp</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
