<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ slack - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ slack - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 27 May 2026 16:22:17 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/slack/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ What are Github Actions and How Can You Automate Tests and Slack Notifications? ]]>
                </title>
                <description>
                    <![CDATA[ Automation is a powerful tool. It both saves us time and can help reduce human error.  But automation can be tough and can sometimes prove to be costly. How can Github Actions help harden our code and give us more time to work on features instead of ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-are-github-actions-and-how-can-you-automate-tests-and-slack-notifications/</link>
                <guid isPermaLink="false">66b8e39047c23b7ae1ad0bdf</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ automation testing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CI/CD ]]>
                    </category>
                
                    <category>
                        <![CDATA[ continuous delivery ]]>
                    </category>
                
                    <category>
                        <![CDATA[ continuous deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Continuous Integration ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub Actions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Colby Fayock ]]>
                </dc:creator>
                <pubDate>Wed, 03 Jun 2020 14:45:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/05/github-actions.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Automation is a powerful tool. It both saves us time and can help reduce human error. </p>
<p>But automation can be tough and can sometimes prove to be costly. How can Github Actions help harden our code and give us more time to work on features instead of bugs?</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-are-github-actions">What are Github Actions?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-cicd">What is CI/CD?</a></li>
<li><a class="post-section-overview" href="#heading-what-are-we-going-to-build">What are we going to build?</a></li>
<li><a class="post-section-overview" href="#heading-part-0-setting-up-a-project">Part 0: Setting up a project</a></li>
<li><a class="post-section-overview" href="#heading-part-1-automating-tests">Part 1: Automating tests</a></li>
<li><a class="post-section-overview" href="#heading-part-2-post-new-pull-requests-to-slack">Part 2: Post new pull requests to Slack</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/1n-jHHNSoTw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-what-are-github-actions">What are Github Actions?</h2>
<p><a target="_blank" href="https://github.com/features/actions">Actions</a> are a relatively new feature to <a target="_blank" href="https://github.com/">Github</a> that allow you to set up CI/CD workflows using a configuration file right in your Github repo.</p>
<p>Previously, if you wanted to set up any kind of automation with tests, builds, or deployments, you would have to look to services like <a target="_blank" href="https://circleci.com/">Circle CI</a> and <a target="_blank" href="https://travis-ci.org/">Travis</a> or write your own scripts. But with Actions, you have first class support to powerful tooling to automate your workflow.</p>
<h2 id="heading-what-is-cicd">What is CI/CD?</h2>
<p>CD/CD stands for Continuous Integration and Continuous Deployment (or can be Continuous Delivery). They're both practices in software development that allow teams to build projects together quickly, efficiently, and ideally with less errors.</p>
<p>Continuous Integration is the idea that as different members of the team work on code on different git branches, the code is merged to a single working branch which is then built and tested with automated workflows. This helps to constantly make sure everyone's code is working properly together and is well-tested.</p>
<p>Continuous Deployment takes this a step further and takes this automation to the deployment level. Where with the CI process, you automate the testing and the building, Continuous Deployment will automate deploying the project to an environment. </p>
<p>The idea is that the code, once through any building and testing processes, is in a deployable state, so it should be able to be deployed.</p>
<h2 id="heading-what-are-we-going-to-build">What are we going to build?</h2>
<p>We're going to tackle two different workflows.</p>
<p>The first will be to simply run some automated tests that will prevent a pull request from being merged if it is failing. We won't walk through building the tests, but we'll walk through running tests that already exist.</p>
<p>In the second part, we'll set up a workflow that sends a message to slack with a link to a pull request whenever a new one is created. This can be super helpful when working on open source projects with a team and you need a way to keep track of requests.</p>
<h2 id="heading-part-0-setting-up-a-project">Part 0: Setting up a project</h2>
<p>For this guide, you can really work through any node-based project as long as it has tests you can run for Part 1.</p>
<p>If you'd like to follow along with a simpler example that I'll be using, I've set up a new project that you can clone with a single function that has two tests that are able to run and pass.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/function-with-test.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>A function with two tests</em></p>
<p>If you'd like to check out this code to get started, you can run:</p>
<pre><code class="lang-shell">git clone --single-branch --branch start git@github.com:colbyfayock/my-github-actions.git
</code></pre>
<p>Once you have that cloned locally and have installed the dependencies, you should be able to run the tests and see them pass!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/passing-tests.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Passing tests</em></p>
<p>It should also be noted that you'll be required to have this project added as a new repository on Github in order to follow along.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-github-actions/commit/6919b1b9beea4823fd28375f1864d233e23f2d26">Follow along with the commit!</a></p>
<h2 id="heading-part-1-automating-tests">Part 1: Automating tests</h2>
<p>Tests are an important part of any project that allow us to make sure we're not breaking existing code while we work. While they're important, they're also easy to forget about.</p>
<p>We can remove the human nature out of the equation and automate running our tests to make sure we can't proceed without fixing what we broke.</p>
<h3 id="heading-step-1-creating-a-new-action">Step 1: Creating a new action</h3>
<p>The good news, is Github actually makes it really easy to get this workflow started as it comes as one of their pre-baked options.</p>
<p>We'll start by navigating to the <strong>Actions</strong> tab on our repository page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-actions-dashboard.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Github Actions starting page</em></p>
<p>Once there, we'll immediately see some starter workflows that Github provides for us to dive in with. Since we're using a node project, we can go ahead and click <strong>Set up this workflow</strong> under the <strong>Node.js</strong> workflow.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-action-new-nodejs-workflow.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up a Node.js Github Action workflow</em></p>
<p>After the page loads, Github will land you on a new file editor that already has a bunch of configuration options added.</p>
<p>We're actually going to leave this "as is" for our first step. Optionally, you can change the name of the file to <code>tests.yml</code> or something you'll remember.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-action-create-new-workflow.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding a new Github Action workflow file</em></p>
<p>You can go ahead and click <strong>Start commit</strong> then either commit it directory to the <code>master</code> branch or add the change to a new branch. For this walkthrough, I'll be committing straight to <code>master</code>.</p>
<p>To see our new action run, we can again click on the <strong>Actions</strong> tab which will navigate us back to our new Actions dashboard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-action-workflow-status.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Viewing Github Action workflow events</em></p>
<p>From there, you can click on <strong>Node.js CI</strong> and select the commit that you just made above and you'll land on our new action dashboard. You can then click one of the node versions in the sidebar via <strong>build (#.x)</strong>, click the <strong>Run npm test</strong> dropdown, and we'll be able to see the output of our tests being run (which if you're following along with me, should pass!).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-action-workflow-logs.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Viewing logs of a Github Action workflow</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-github-actions/commit/10e397966572ed9975cac40f6ab5f41c1255a947">Follow along with the commit!</a></p>
<h3 id="heading-step-2-configuring-our-new-action">Step 2: Configuring our new action</h3>
<p>So what did we just do above? We'll walk through the configuration file and what we can customize.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-action-workflow-file.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Github Action Node.js workflow file</em></p>
<p>Starting from the top, we specify our name:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Node.js</span> <span class="hljs-string">CI</span>
</code></pre>
<p>This can really be whatever you want. Whatever you pick should help you remember what it is. I'm going to customize this to "Tests" so I know exactly what's going on.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">master</span> ]
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">master</span> ]
</code></pre>
<p>The <code>on</code> key is how we specify what events trigger our action. This can be a variety of things like based on time with <a target="_blank" href="https://en.wikipedia.org/wiki/Cron">cron</a>. But here, we're saying that we want this action to run any time someone pushes commits to  <code>master</code> or someone creates a pull request targeting the <code>master</code> branch. We're not going to make a change here.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
</code></pre>
<p>This next bit creates a new job called <code>build</code>. Here we're saying that we want to use the latest version of Ubuntu to run our tests on. <a target="_blank" href="https://ubuntu.com/">Ubuntu</a> is common, so you'll only want to customize this if you want to run it on a specific environment.</p>
<pre><code class="lang-yaml">    <span class="hljs-attr">strategy:</span>
      <span class="hljs-attr">matrix:</span>
        <span class="hljs-attr">node-version:</span> [<span class="hljs-number">10.</span><span class="hljs-string">x</span>, <span class="hljs-number">12.</span><span class="hljs-string">x</span>, <span class="hljs-number">14.</span><span class="hljs-string">x</span>]
</code></pre>
<p>Inside of our job we specify a <a target="_blank" href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstrategy">strategy</a> matrix. This allows us to run the same tests on a few different variations. </p>
<p>In this instance, we're running the tests on 3 different versions of <a target="_blank" href="https://nodejs.org/en/">node</a> to make sure it works on all of them. This is definitely helpful to make sure your code is flexible and future proof, but if you're building and running your code on a specific node version, you're safe to change this to only that version.</p>
<pre><code class="lang-yaml">    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Use</span> <span class="hljs-string">Node.js</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v1</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">node-version:</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">build</span> <span class="hljs-string">--if-present</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>
</code></pre>
<p>Finally, we specify the steps we want our job to run. Breaking this down:</p>
<ul>
<li><code>uses: actions/checkout@v2</code>: In order for us to run our code, we need to have it available. This checks out our code on our job environment so we can use it to run tests.</li>
<li><code>uses: actions/setup-node@v1</code>: Since we're using node with our project, we'll need it set up on our environment. We're using this action to do that setup  for us for each version we've specified in the matrix we configured above.</li>
<li><code>run: npm ci</code>: If you're not familiar with <code>npm ci</code>, it's similar to running <code>npm install</code> but uses the <code>package-lock.json</code> file without performing any patch upgrades. So essentially, this installs our dependencies.</li>
<li><code>run: npm run build --if-present</code>: <code>npm run build</code> runs the build script in our project. The <code>--if-present</code> flag performs what it sounds like and only runs this command if the build script is present. It doesn't hurt anything to leave this in as it won't run without the script, but feel free to remove this as we're not building the project here.</li>
<li><code>run: npm test</code>: Finally, we run <code>npm test</code> to run our tests. This uses the <code>test</code> npm script set up in our <code>package.json</code> file.</li>
</ul>
<p>And with that, we've made a few tweaks, but our tests should run after we've committed those changes and pass like before!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-action-workflow-logs-npm-test.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Logs of passing tests in Github Action workflow</em></p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-github-actions/commit/087cd8e8592d1f2b520b6e44b70b0a242a9d2d72">Follow along with the commit!</a></p>
<h3 id="heading-step-3-testing-that-our-tests-fail-and-prevent-merges">Step 3: Testing that our tests fail and prevent merges</h3>
<p>Now that our tests are set up to automatically run, let's try to break it to see it work.</p>
<p>At this point, you can really do whatever you want to intentionally break the tests, but <a target="_blank" href="https://github.com/colbyfayock/my-github-actions/pull/1">here's what I did</a>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/bad-changes-code-diff.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Code diff - https://github.com/colbyfayock/my-github-actions/pull/1</em></p>
<p>I'm intentionally returning different expected output so that my tests will fail. And they do!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-failing-checks.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Failing status checks on pull request</em></p>
<p>In my new pull request, my new branch breaks the tests, so it tells me my checks have failed. If you noticed though, it's still green to merge, so how can we prevent merges?</p>
<p>We can prevent pull requests from being merged by setting up a Protected Branch in our project settings.</p>
<p>First, navigate to <strong>Settings</strong>, then <strong>Branches</strong>, and click <strong>Add rule</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-add-protected-branch.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Github branch protection rules</em></p>
<p>We'll then want to set the branch name pattern to <code>*</code>, which means all branches, check the <strong>Require status checks to pass before merging option</strong>, then select all of our different status checks that we'd like to require to pass before merging.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-configure-protected-branch.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up a branch protection rule in Github</em></p>
<p>Finally, hit <strong>Create</strong> at the bottom of the page.</p>
<p>And once you navigate back to the pull request, you'll notice that the messaging is a bit different and states that we need our statuses to pass before we can merge.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-failing-checks-cant-merge.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Failing tests preventing merge in pull request</em></p>
<p><em>Note: as an administrator of a repository, you'll still be able to merge, so this technically only prevents non-administrators from merging. But will give you increased messaging if the tests fail.</em></p>
<p>And with that, we have a new Github Action that runs our tests and prevents pull requests from merging if they fail.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-github-actions/pull/1">Follow along with the pull request!</a></p>
<p><em>Note: we won't be merging that pull request before continuing to Part 2.</em></p>
<h2 id="heading-part-2-post-new-pull-requests-to-slack">Part 2: Post new pull requests to Slack</h2>
<p>Now that we're preventing merge requests if they're failing, we want to post a message to our <a target="_blank" href="http://slack.com/">Slack</a> workspace whenever a new pull request is opened up. This will help us keep tabs on our repos right in Slack.</p>
<p>For this part of the guide, you'll need a Slack workspace that you have permissions to create a new developer app with and the ability to create a new channel for the bot user that will be associated with that app.</p>
<h3 id="heading-step-1-setting-up-slack">Step 1: Setting up Slack</h3>
<p>There are a few things we're going to walk through as we set up Slack for our workflow:</p>
<ul>
<li>Create a new app for our workspace</li>
<li>Assign our bot permissions</li>
<li>Install our bot to our workspace</li>
<li>Invite our new bot to our channel</li>
</ul>
<p>To get started, we'll create a new app. Head over to the <a target="_blank" href="https://api.slack.com/apps">Slack API Apps dashboard</a>. If you already haven't, log in to your Slack account with the Workspace you'd like to set this up with.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/slack-create-new-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Creating a new Slack app</em></p>
<p>Now, click <strong>Create New App</strong> where you'll be prompted to put in a name and select a workspace you want this app to be created for. I'm going to call my app "Gitbot" as the name, but you can choose whatever makes sense for you. Then click <strong>Create App</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/slack-add-name-new-app.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Configuring a new Slack app</em></p>
<p>Once created, navigate to the <strong>App Home</strong> link in the left sidebar. In order to use our bot, we need to assign it <a target="_blank" href="https://oauth.net/">OAuth</a> scopes so it has permissions to work in our channel, so select <strong>Review Scopes to Add</strong> on that page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/slack-app-review-scopes.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Reviewing Slack app scopes</em></p>
<p>Scroll own and you'll see a <strong>Scopes</strong> section and under that a <strong>Bot Token</strong> section. Here, click <strong>Add an OAuth Scope</strong>. For our bot, we don't need a ton of permissions, so add the <code>channels:join</code> and <code>chat:write</code> scopes and we should be good to go.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/slack-app-add-scopes.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Adding scopes for a Slack app Bot Token</em></p>
<p>Now that we have our scopes, let's add our bot to our workspace. Scroll up on that same page to the top and you'll see a button that says <strong>Install App to Workspace</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/slack-install-app-to-workspace.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Installing Slack app to a workspace</em></p>
<p>Once you click this, you'll be redirected to an authorization page. Here, you can see the scopes we selected for our bot. Next, click <strong>Allow</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/slack-app-allow-workspace-permissions.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Allowing permission for Slack app to be installed to workspace</em></p>
<p>At this point, our Slack bot is ready to go. At the top of the <strong>OAuth &amp; Permissions</strong> page, you'll see a <strong>Bot User OAuth Access Token</strong>. This is what we'll use when setting up our workflow, so either copy and save this token or remember this location so you know how to find it later.</p>
<p><em>Note: this token is private - don't give this out, show it in a screencast, or let anyone see it!</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/slack-app-oauth-token.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Copying OAuth Access Token for Slack bot user</em></p>
<p>Finally, we need to invite our Slack bot to our channel. If you open up your workspace, you can either use an existing channel or create a new channel for these notifications, but you'll want to enter the command <code>/invite @[botname]</code> which will invite our bot to our channel.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/slack-invite-bot-to-channel.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Inviting Slack bot user to channel</em></p>
<p>And once added, we're done with setting up Slack!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/slack-app-bot-joined-channel.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Slack bot was added to channel</em></p>
<h3 id="heading-create-a-github-action-to-notify-slack">Create a Github Action to notify Slack</h3>
<p>Our next step will be somewhat similar to when we created our first Github Action. We'll create a workflow file which we'll configure to send our notifications.</p>
<p>While we can use our code editors to do this by creating a file in the <code>.github</code> directory, I'm going to use the Github UI.</p>
<p>First, let's navigate back to our <em>Actions</em> tab in our repository. Once there, select <strong>New workflow</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-new-workflow.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up a new Github Action workflow</em></p>
<p>This time, we're going to start the workflow manually instead of using a pre-made Action. Select <strong>set up a workflow yourself</strong> at the top.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-set-up-new-workflow.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up a Github Action workflow manually</em></p>
<p>Once the new page loads, you'll be dropped in to a new template where we can start working. Here's what our new workflow will look like:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Slack</span> <span class="hljs-string">Notifications</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">master</span> ]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">notifySlack:</span>

    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Notify</span> <span class="hljs-string">slack</span>
      <span class="hljs-attr">env:</span>
        <span class="hljs-attr">SLACK_BOT_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SLACK_BOT_TOKEN</span> <span class="hljs-string">}}</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">abinoda/slack-action@master</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">args:</span> <span class="hljs-string">'{\"channel\":\"[Channel ID]\",\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"*Pull Request:* $<span class="hljs-template-variable">{{ github.event.pull_request.title }}</span>\"}},{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"*Who?:* $<span class="hljs-template-variable">{{ github.event.pull_request.user.login }}</span>\n*Request State:* $<span class="hljs-template-variable">{{ github.event.pull_request.state }}</span>\"}},{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"&lt;$<span class="hljs-template-variable">{{ github.event.pull_request.html_url }}</span>|View Pull Request&gt;\"}}]}'</span>
</code></pre>
<p>So what's happening in the above?</p>
<ul>
<li><code>name</code>: we're setting a friendly name for our workflow</li>
<li><code>on</code>: we want our workflow to trigger when there's a pull request is created that targets our <code>master</code> branch</li>
<li><code>jobs</code>: we're creating a new job called <code>notifySlack</code></li>
<li><code>jobs.notifySlack.runs-on</code>: we want our job to run on a basic setup of the latest Unbuntu</li>
<li><code>jobs.notifySlack.steps</code>: we really only have one step here - we're using a pre-existing Github Action called <a target="_blank" href="https://github.com/marketplace/actions/post-slack-message">Slack Action</a> and we're configuring it to publish a notification to our Slack</li>
</ul>
<p>There are two points here we'll need to pay attention to, the <code>env.SLACK_BOT_TOKEN</code> and the <code>with.args</code>.</p>
<p>In order for Github to communicate with Slack, we'll need a token. This is what we're setting in <code>env.SLACK_BOT_TOKEN</code>. We generated this token in the first step. Now that we'll be using this in our workflow configuration, we'll need to <a target="_blank" href="https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets-for-a-repository">add it as a Git Secret in our project</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-slack-token-secret.jpg" alt="Image" width="600" height="400" loading="lazy">
_Github secrets including SLACK_BOT<em>TOKEN</em></p>
<p>The  <code>with.args</code> property is what we use to configure the payload to the Slack API that includes the channel ID (<code>channel</code>) and our actual message (<code>blocks</code>).</p>
<p>The payload in the arguments is stringified and escaped. For example, when expanded it looks like this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"channel"</span>: <span class="hljs-string">"[Channel ID]"</span>,
  <span class="hljs-attr">"blocks"</span>: [{
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"section"</span>,
    <span class="hljs-attr">"text"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"mrkdwn"</span>,
      <span class="hljs-attr">"text"</span>: <span class="hljs-string">"*Pull Request:* ${{ github.event.pull_request.title }}"</span>
    }
  }, {
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"section"</span>,
    <span class="hljs-attr">"text"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"mrkdwn"</span>,
      <span class="hljs-attr">"text"</span>: <span class="hljs-string">"*Who?:*n${{ github.event.pull_request.user.login }}n*State:*n${{ github.event.pull_request.state }}"</span>
    }
  }, {
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"section"</span>,
    <span class="hljs-attr">"text"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"mrkdwn"</span>,
      <span class="hljs-attr">"text"</span>: <span class="hljs-string">"&lt;${{ github.event.pull_request._links.html.href }}|View Pull Request&gt;"</span>
    }
  }]
}
</code></pre>
<p><em>Note: this is just to show what the content looks like, we need to use the original file with the stringified and escaped argument.</em></p>
<p>Back to our configuration file, the first thing we set is our channel ID. To find our channel ID, you'll need to use the Slack web interface. Once you open Slack in your browser, you want to find your channel ID in the URL:</p>
<pre><code>https:<span class="hljs-comment">//app.slack.com/client/[workspace ID]/[channel ID]</span>
</code></pre><p><img src="https://www.freecodecamp.org/news/content/images/2020/05/slack-web-channel-id.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Channel ID in Slack web app URL</em></p>
<p>With that channel ID, you can modify our workflow configuration and replace <code>[Channel ID]</code> with that ID:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">with:</span>
  <span class="hljs-attr">args:</span> <span class="hljs-string">'{\"channel\":\"C014RMKG6H2\",...</span>
</code></pre>
<p>The rest of the arguments property is how we set up our message. It includes variables from the Github event that we use to customize our message. </p>
<p>We won't go into tweaking that here, as what we already have will send a basic pull request message, but you can test out and build your own payload with Slack's <a target="_blank" href="https://app.slack.com/block-kit-builder/">Block Kit Builder</a>.</p>
<p><a target="_blank" href="https://github.com/colbyfayock/my-github-actions/commit/e228b9899ef3da218d1a100d06a72259d45ea19e">Follow along with the commit!</a></p>
<h3 id="heading-test-out-our-slack-workflow">Test out our Slack workflow</h3>
<p>So now we have our workflow configured with our Slack app, finally we're ready to use our bot!</p>
<p>For this part, all we need to do is create a new pull request with any change we want. To test this out, I simply <a target="_blank" href="https://github.com/colbyfayock/my-github-actions/pull/2">created a new branch</a> where I added a sentence to the <code>README.md</code> file.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/github-test-pull-request.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Code diff - <a target="_blank" href="https://github.com/colbyfayock/my-github-actions/pull/2">https://github.com/colbyfayock/my-github-actions/pull/2</a></em></p>
<p>Once you <a target="_blank" href="https://github.com/colbyfayock/my-github-actions/pull/2">create that pull request</a>, similar to our tests workflow, Github will run our Slack workflow! You can see this running in the Actions tab just like before.</p>
<p>As long as you set everything up correctly, once the workflow runs, you should now have a new message in Slack from your new bot.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/05/slack-github-notification.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Slack bot automated message about new pull request</em></p>
<p><em>Note: we won't be merging that pull request in.</em></p>
<h2 id="heading-what-else-can-we-do">What else can we do?</h2>
<h3 id="heading-customize-your-slack-notifications">Customize your Slack notifications</h3>
<p>The message I put together is simple. It tells us who created the pull request and gives us a link to it.</p>
<p>To customize the formatting and messaging, you can use the Github <a target="_blank" href="https://app.slack.com/block-kit-builder/">Block Kit Builder</a> to create your own.</p>
<p>If you'd like to include additional details like the variables I used for the pull request, you can make use of Github's available <a target="_blank" href="https://help.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#contexts">contexts</a>. This lets you pull information about the environment and the job to customize your message.</p>
<p>I couldn't seem to find any sample payloads, so here's an example of a sample <code>github</code> context payload you would expect in the event.</p>
<p><a target="_blank" href="https://gist.github.com/colbyfayock/1710edb9f47ceda0569844f791403e7e">Sample github context</a></p>
<h3 id="heading-more-github-actions">More Github actions</h3>
<p>With our ability to create new custom workflows, that's not a lot we can't automate. Github even has a <a target="_blank" href="https://github.com/marketplace?type=actions">marketplace</a> where you can browse around for one.</p>
<p>If you're feeling like taking it a step further, you can even create your own! This lets you set up scripts to configure a workflow to perform whatever tasks you need for your project.</p>
<h2 id="heading-join-in-the-conversation">Join in the conversation!</h2>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/colbyfayock/status/1268197100539514881"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h2 id="heading-what-do-you-use-github-actions-for">What do you use Github actions for?</h2>
<p>Share with me on <a target="_blank" href="https://twitter.com/colbyfayock">Twitter</a>!</p>
<div id="colbyfayock-author-card">
  <p>
    <a href="https://twitter.com/colbyfayock">
      <img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy">
    </a>
  </p>
  <ul>
    <li>
      <a href="https://twitter.com/colbyfayock">? Follow Me On Twitter</a>
    </li>
    <li>
      <a href="https://youtube.com/colbyfayock">?️ Subscribe To My Youtube</a>
    </li>
    <li>
      <a href="https://www.colbyfayock.com/newsletter/">✉️ Sign Up For My Newsletter</a>
    </li>
  </ul>
</div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to build a SlackBot with Node.js and SlackBots.js ]]>
                </title>
                <description>
                    <![CDATA[ Slack is an American cloud-based set of proprietary team collaboration software tools and online services, developed by Slack Technologies. Slack is a workspace where teams can communicate and collaborate. Teamwork in Slack happens in channels — a si... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/building-a-slackbot-with-node-js-and-slackbots-js/</link>
                <guid isPermaLink="false">66d84e0d7211ea6be29e1b41</guid>
                
                    <category>
                        <![CDATA[ 100DaysOfCode ]]>
                    </category>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bolaji Ayodeji ]]>
                </dc:creator>
                <pubDate>Mon, 12 Aug 2019 14:01:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/05/article-banner--4-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Slack is an American cloud-based set of proprietary team collaboration software tools and online services, developed by Slack Technologies. Slack is a workspace where teams can communicate and collaborate.</p>
<p>Teamwork in Slack happens in channels — a single place for messaging, tools and files — helping everyone save time and collaborate.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/organize-conversations-slack-product-desktop.png" alt="Image" width="600" height="400" loading="lazy"></p>
<hr>
<p>One of the awesome features of Slack is <a target="_blank" href="https://slack.com/apps">Slack Apps</a>, integrations and <a target="_blank" href="https://api.slack.com/bot-users">Slack Bots</a>.</p>
<p>A Slack bot is a type of Slack App designed to interact with users via conversation. Your bot can send DMs, it can be mentioned by users, it can post messages or upload files, and it can be invited to channels. Cool right?</p>
<p>If you use Slack already, you should be familiar with some creative Slack bots like <a target="_blank" href="https://standupbot.com/">Standupbot</a>, <a target="_blank" href="https://birthdaybot.io/">Birthdaybot</a> and more.</p>
<p>In this article, I'll walk you through building your first Slack bot from start to finish with <a target="_blank" href="http://nodejs.org/">Node.js</a> and <a target="_blank" href="https://github.com/mishk0/slack-bot-api">SlackBots.js</a></p>
<blockquote>
<p>PS: This article was published <a target="_blank" href="https://www.bolajiayodeji.com/building-a-slackbot-with-node-js-and-slackbots-js/">on my blog first</a>.</p>
</blockquote>
<h1 id="heading-slackbot-description">SlackBot Description</h1>
<p>We're going to build a simple Slackbot that displays random inspiring techie quotes and jokes for developers/designers.</p>
<p>I built a <a target="_blank" href="https://github.com/BolajiAyodeji/inspireNuggets">chrome extension</a> that displays random inspiring techie quotes for developers/designers on your new tab (you can download it <a target="_blank" href="https://chrome.google.com/webstore/detail/inspirenuggets-for-chrome/acnfgdioohhajabdofaadfdhmlkphmlb">here</a>). We'll be using the quotes JSON from this extension as our quotes API and the <a target="_blank" href="https://api.chucknorris.io/">Chuck Norris Jokes API</a> for the jokes.</p>
<p>When a user mentions our bot and adds <strong>inspire me</strong>, the bot returns a random quote from <a target="_blank" href="https://chrome.google.com/webstore/detail/inspirenuggets-for-chrome/acnfgdioohhajabdofaadfdhmlkphmlb">inspireNuggets</a>. When the user types <strong>random joke</strong>, it returns a random joke from the <a target="_blank" href="https://api.chucknorris.io/">Chuck Norris</a> API. And when the user types help, it returns the instruction guide.</p>
<blockquote>
<p>@inspirenuggets inspire me</p>
<p>@inspirenuggets random joke</p>
<p>@inspirenuggets help</p>
</blockquote>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This article is not really about what we'll be building - it's just to show you the concept behind Slack bots and how to build yours. After you go through it, you can think about something else and build a different bot, as there're many possibilities.</p>
<p>You can clone or fork the final project <a target="_blank" href="https://github.com/BolajiAyodeji/inspireNuggetsSlackBot">here</a>.</p>
<p>Pretty interesting right? Let's get started.</p>
<h1 id="heading-prerequisites">Prerequisites</h1>
<p>We'll build this bot with Node.js and SlackBots.js. You don't need to know how to write Node.js, since I'll walk you through it. Still, knowing it is an advantage. You should also have</p>
<ul>
<li><p>Basic JavaScript knowledge</p>
</li>
<li><p>ES6 JavaScript</p>
</li>
<li><p>Slack workspace</p>
</li>
<li><p>Some experience with Slack</p>
</li>
<li><p>Some version control skills</p>
</li>
</ul>
<h1 id="heading-setup-environment">Setup environment</h1>
<p>Let's set up and install Node.js and Npm first.</p>
<ul>
<li><p>Download node <a target="_blank" href="https://nodejs.org/en/">here</a>. If you have it installed already, skip this step. If you prefer to use a package manager to install, read <a target="_blank" href="https://nodejs.org/en/download/package-manager/#windows">this</a> for all operating systems.</p>
</li>
<li><p>Check if you have Node installed</p>
</li>
</ul>
<pre><code class="lang-python">node -v
</code></pre>
<ul>
<li>Node.js comes with Npm, so you don't have to install that again.</li>
</ul>
<pre><code class="lang-python">npm -v
</code></pre>
<p>Now that we have Node.js setup, let's initialize our project.</p>
<p>Create your project directory (I called mine Slackbot) and initialize git:</p>
<pre><code class="lang-python">git init
</code></pre>
<p>Next, create an <code>index.js</code> file:</p>
<pre><code class="lang-python">touch index.js
</code></pre>
<p>And initialize Npm:</p>
<pre><code class="lang-python">npm init
</code></pre>
<p>Simply answer all questions that come afterwards. If you're having issues, here's my own <code>package.json</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"slackbot"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"A simple Slackbot that displays random inspiring techie quotes for developers/designers."</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"index.js"</span>
  },
  <span class="hljs-attr">"repository"</span>: {
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"git"</span>,
    <span class="hljs-attr">"url"</span>: <span class="hljs-string">"git+https://github.com/BolajiAyodeji/slackbot.git"</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Bolaji Ayodeji"</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"MIT"</span>,
  <span class="hljs-attr">"bugs"</span>: {
    <span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://github.com/BolajiAyodeji/slackbot/issues"</span>
  },
  <span class="hljs-attr">"homepage"</span>: <span class="hljs-string">"https://github.com/BolajiAyodeji/slackbot#readme"</span>
}
</code></pre>
<h1 id="heading-install-dependencies">Install Dependencies</h1>
<p><strong>Now let's install and setup all the libraries we need.</strong></p>
<h2 id="heading-slackbotsjs">SlackBots.js</h2>
<p><a target="_blank" href="https://github.com/mishk0/slack-bot-api">SlackBots.js</a> is a Node.js library for easy operation with the Slack API.</p>
<pre><code class="lang-python">npm install slackbots
</code></pre>
<p>In <code>index.js</code>:</p>
<pre><code class="lang-python">const SlackBot = require(<span class="hljs-string">'slackbots'</span>);
</code></pre>
<h2 id="heading-axios">Axios</h2>
<p><a target="_blank" href="https://github.com/axios/axios">Axios</a> is a promise-based HTTP client for the browser and node.js. If you know Fetch or AJAX, this is just a library that does the same thing with way cooler features. You can see them <a target="_blank" href="https://github.com/axios/axios">here</a>.</p>
<pre><code class="lang-python">npm install axios
</code></pre>
<p>In <code>index.js</code>:</p>
<pre><code class="lang-python">const axios = require(<span class="hljs-string">'axios'</span>)
</code></pre>
<h2 id="heading-nodemon">Nodemon</h2>
<p>To run a script in Node.js, you have to run <code>node index.js</code>. Whenever you make changes to this file, you have to rerun <code>node index.js</code>. This sucks when you're making so many changes like we'll be doing. That's why we need <a target="_blank" href="https://github.com/remy/nodemon">nodemon</a>, a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected.</p>
<pre><code class="lang-python">npm install -g nodemon
</code></pre>
<p>In <code>package.json</code>, locate the scripts section and add a new start script:</p>
<pre><code class="lang-python"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"node index.js"</span>
  }
</code></pre>
<p>If you run <code>npm start</code>, the file will run but won't restart on change. To fix this, use the nodemon we installed instead of node like so:</p>
<pre><code class="lang-python"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"nodemon index.js"</span>
  }
</code></pre>
<h1 id="heading-dotenv">Dotenv</h1>
<p>I won't explain this in-depth. In a few days, I'll publish an article around environmental variables, but for now just know that we use this to hide secret keys and tokens like the Slack Access Token we would be using. This way you don't have to push your secret keys to GitHub.</p>
<p>There are several ways to do this, but I prefer using dotenv. <a target="_blank" href="https://github.com/motdotla/dotenv">Dotenv</a> is a zero-dependency module that loads environment variables from a .env file into process.env.</p>
<pre><code class="lang-python">npm install dotenv
</code></pre>
<p>In <code>index.js</code>:</p>
<pre><code class="lang-python">const dotenv = require(<span class="hljs-string">'dotenv'</span>)

dotenv.config()
</code></pre>
<p>After all installation, your <code>package.json</code> should look like this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"inspireNuggetsSlackBot"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"A simple Slackbot that displays random inspiring techie quotes and jokes for developers/designers."</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"nodemon index.js"</span>
  },
  <span class="hljs-attr">"repository"</span>: {
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"git"</span>,
    <span class="hljs-attr">"url"</span>: <span class="hljs-string">"git+https://github.com/BolajiAyodeji/inspireNuggetsSlackBot.git"</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Bolaji Ayodeji"</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"MIT"</span>,
  <span class="hljs-attr">"bugs"</span>: {
    <span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://github.com/BolajiAyodeji/inspireNuggetsSlackBot/issues"</span>
  },
  <span class="hljs-attr">"homepage"</span>: <span class="hljs-string">"https://github.com/BolajiAyodeji/inspireNuggetsSlackBot#readme"</span>,
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"dotenv"</span>: <span class="hljs-string">"^8.0.0"</span>
  },
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"axios"</span>: <span class="hljs-string">"^0.19.0"</span>,
    <span class="hljs-attr">"slackbots"</span>: <span class="hljs-string">"^1.2.0"</span>
  }
}
</code></pre>
<h1 id="heading-create-your-slack-workspace">Create your Slack workspace</h1>
<p>Now that we have that all set up, we need a Slack workspace to run our bot in development. Creating a workspace is pretty easy, read <a target="_blank" href="https://get.slack.help/hc/en-us/articles/206845317-Create-a-Slack-workspace">this</a> to learn more.</p>
<h1 id="heading-register-your-slack-bot">Register your Slack Bot</h1>
<p>Now that you have a workspace, you should have a Slack URL with your workspace name. Mine is <code>mekafindteam.slack.com</code>.</p>
<p>Now you'll need to create a Slack App. Create one <a target="_blank" href="https://api.slack.com/apps/new">here</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture5.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Enter your App name and ensure you're in the workspace you created if you're in multiple workspaces.</p>
<p>Now you'll see the settings &gt; Basic Information page. Click the first tab <code>Add features and functionality</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture6.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Since we're building a bot, select the <strong>Bots</strong> field.</p>
<p>Now you'll see the Bot user page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture7.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click the <code>Add a Bot User</code> button.</p>
<p>Your display name will automatically be filled in from your already chosen App name. You can update it, but I'll advise you use the same name everywhere with the same alphabet case to avoid errors.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture8.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now, toggle the <code>Always Show My Bot as Online</code> switch to always show your bot as Online. Remember this bot is just like a user in your workspace. Afterwards, click the <code>Add Bot User</code> button.</p>
<p>Save all changes now:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture9.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, return to the <code>Basic Information</code> page and select the <code>Install your app to your workspace</code> tab.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture10.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click the <code>Install App to Workspace</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture11.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click allow and wait to be redirected back to the <code>Basic Information</code> page.</p>
<p>Note the <code>Manage distribution</code> tab: this section is needed when you want to make your Bot available for installation by others. For now we're just building in development and I won't be covering distribution in this article. In my next article, I'll show you how to deploy your Slack bot and make it available as an App to other workspaces.</p>
<p>If you check your Slack workspace now, you should see the App installed in the Apps section.</p>
<p>For now, it's offline - once we start building the bot, we'll turn this on.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture15.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h1 id="heading-customize-your-slack-bot">Customize your Slack bot</h1>
<p>Now we've created our bot, let's do some customization.</p>
<p>Still, on the <code>Basic Information</code> page, scroll down to the <code>Display Information</code> section:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture12.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This is basic stuff: just upload a logo, change your background color, and add a short description.</p>
<p>Your icon should be <code>512x512px</code> or bigger and your background color should be in HEX. Read more on the App guidelines <a target="_blank" href="https://api.slack.com/docs/slack-apps-guidelines">here</a>.</p>
<p>Here's what mine looks like after customization:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture13.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h1 id="heading-slack-bot-oauth-tokens">Slack bot OAuth Tokens</h1>
<p>Now that we have our Slack bot setup, let's grab out token keys.</p>
<p>In the navigation bar, locate the Features section and click the <code>OAuth &amp; Permission</code> tab:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture16.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You'll see two Access Tokens:</p>
<ul>
<li><p>OAuth Access Token</p>
</li>
<li><p>Bot User OAuth Access Token</p>
</li>
</ul>
<p>Copy the <strong>Bot User OAuth Access Token.</strong></p>
<p>This will change every time you re-install this app or when you install it in another workspace. The token should start with <code>xoxb-</code>.</p>
<blockquote>
<p>Keeping credentials secure is important whether you're developing open source libraries and tools, internal integrations for your workspace, or Slack apps for distribution to workspaces across the world. - Slack</p>
</blockquote>
<p>This is why we have installed Dotenv - we'll set that up in the next section.</p>
<h1 id="heading-building-the-bot">Building the bot</h1>
<p>Now let's build our bot :).</p>
<h3 id="heading-first-lets-keep-our-access-token-somewhere">First, let's keep our Access Token somewhere.</h3>
<p>Create a <code>.env</code> file and add this:</p>
<pre><code class="lang-python">BOT_TOKEN=YOUR_SLACK_ACCESS_TOKEN_HERE
</code></pre>
<p>Now let's start our SlackBot.js:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> bot = <span class="hljs-keyword">new</span> SlackBot({
    <span class="hljs-attr">token</span>: <span class="hljs-string">`<span class="hljs-subst">${process.env.BOT_TOKEN}</span>`</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">'inspirenuggets'</span>
})
</code></pre>
<p>We've just created a bot variable that initializes a new SlackBot instance which has two values, our token and app name.</p>
<p>I used the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals">ES6 template string syntax</a> to bring in our token key from our <code>.env</code> file. dotenv has this covered for us.</p>
<p>Make sure you use the same name you used while creating your Slack app, or else you'll have authentication errors.</p>
<p>Now start the app:</p>
<pre><code class="lang-python">npm start
</code></pre>
<p>nodemon should be running now and our Slack app should be online too.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture17.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture18.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-start-handler">Start handler</h3>
<p>Our Bot does nothing now even though it's running. Let's return a message.</p>
<pre><code class="lang-javascript">bot.on(<span class="hljs-string">'start'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> params = {
        <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':robot_face:'</span>
    }

    bot.postMessageToChannel(
        <span class="hljs-string">'random'</span>,
        <span class="hljs-string">'Get inspired while working with @inspirenuggets'</span>,
        params
    );
})
</code></pre>
<p>The <code>bot.on</code> handler sends the welcome message. We passed two parameters, the <code>'start'</code> and a function which holds a params variable which also holds the slack emoji. Slack emoji have codes, and you can find them <a target="_blank" href="https://slackmojis.com/">here</a>. I used <code>:robot_face:</code>, but you can change this to your preferred emoji.</p>
<p>We also initialized the <code>bot.postMessageToChannel</code> function which is a SlackBot.js method to post a message to a channel. In this function, we pass the channel name we want to post to, the message in a string, and the params variable we declared earlier for the emoji. I used the <strong>#random</strong> channel and sent <code>Get inspired while working with @inspirenuggets</code> to it. Your app should restart automatically and your bot should do this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture19.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Cool right?<br>You can also post messages to users and groups.</p>
<pre><code class="lang-javascript">    <span class="hljs-comment">// define existing username instead of 'user_name'</span>
    bot.postMessageToUser(<span class="hljs-string">'user_name'</span>, <span class="hljs-string">'Hello world!'</span>, params); 


    <span class="hljs-comment">// define private group instead of 'private_group', where bot exist</span>
    bot.postMessageToGroup(<span class="hljs-string">'private_group'</span>, <span class="hljs-string">'Hello world!'</span>, params);
</code></pre>
<h3 id="heading-error-handler">Error Handler</h3>
<p>Let's also write a function to check for errors and return them:</p>
<pre><code class="lang-javascript">bot.on(<span class="hljs-string">'error'</span>, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(err);
})
</code></pre>
<h3 id="heading-message-handler">Message Handler</h3>
<p>Now let's build the main bot functionality.</p>
<p>Like I said earlier, we'll be using the quotes JSON from the extension I built as our quotes API. The JSON can be found with this URL: <code>https://raw.githubusercontent.com/BolajiAyodeji/inspireNuggets/master/src/quotes.json</code></p>
<p>When a user mentions our bot and adds <strong>inspire me</strong>, the bot returns a random quote from <a target="_blank" href="https://chrome.google.com/webstore/detail/inspirenuggets-for-chrome/acnfgdioohhajabdofaadfdhmlkphmlb">inspireNuggets</a>. When the user types <strong>random joke</strong>, it returns a random joke from the <a target="_blank" href="https://api.chucknorris.io/">Chuck Norris</a> API. And when the user types <strong>help</strong>, it returns the instruction guide.</p>
<p>First, let's check for our command words from the user message (<strong>inspire me</strong>, <strong>random joke,</strong> and <strong>help</strong>):</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleMessage</span>(<span class="hljs-params">message</span>) </span>{
    <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' inspire me'</span>)) {
        inspireMe()
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' random joke'</span>)) {
        randomJoke()
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' help'</span>)) {
        runHelp()
    }
}
</code></pre>
<p>Now let's create the three function we need</p>
<p><strong>inspireMe()</strong></p>
<p>Our demo JSON is not really an API, it's just some JSON I used in the Chrome Extension. We're only accessing it from GitHub raw contents. You can use any API you prefer, you'll just have to iterate differently to get your data depending on if your API returns an array or object - whichever it returns, it's not a big deal.</p>
<p>Check out my previous articles on:</p>
<ul>
<li><p><a target="_blank" href="https://www.bolajiayodeji.com/manipulating-arrays-in-javascript/">Manipulating Arrays in JavaScript</a> and</p>
</li>
<li><p><a target="_blank" href="https://www.bolajiayodeji.com/iterating-through-javascript-objects-5-techniques-and-performance-tests/">Iterating through JavaScript Objects  -  5 Techniques and Performance Tests.</a></p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">inspireMe</span>(<span class="hljs-params"></span>) </span>{
    axios.get(<span class="hljs-string">'https://raw.githubusercontent.com/BolajiAyodeji/inspireNuggets/master/src/quotes.json'</span>)
      .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
            <span class="hljs-keyword">const</span> quotes = res.data;
            <span class="hljs-keyword">const</span> random = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * quotes.length);
            <span class="hljs-keyword">const</span> quote = quotes[random].quote
            <span class="hljs-keyword">const</span> author = quotes[random].author

            <span class="hljs-keyword">const</span> params = {
                <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':male-technologist:'</span>
            }

            bot.postMessageToChannel(
                <span class="hljs-string">'random'</span>,
                <span class="hljs-string">`:zap: <span class="hljs-subst">${quote}</span> - *<span class="hljs-subst">${author}</span>*`</span>,
                params
            );

      })
}
</code></pre>
<p>We just used Axios to get the JSON file which returns some data:</p>
<pre><code class="lang-json">[
    {
        <span class="hljs-attr">"number"</span>: <span class="hljs-string">"1"</span>,
        <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Von R. Glitschka"</span>,
        <span class="hljs-attr">"quote"</span>: <span class="hljs-string">"The client may be king, but he's not the art director."</span>
    },
    {
        <span class="hljs-attr">"number"</span>: <span class="hljs-string">"2"</span>,
        <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Frank Capra"</span>,
        <span class="hljs-attr">"quote"</span>: <span class="hljs-string">"A hunch is creativity trying to tell you something."</span>
    },
.
.
.
.
]
</code></pre>
<p>This JSON currently contains 210 quotes and I update them frequently. So we want to get a random quote plus the author name every time the user request it. From our Axios response, we just do this:</p>
<pre><code class="lang-javascript">
<span class="hljs-keyword">const</span> quotes = res.data;
<span class="hljs-keyword">const</span> random = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * quotes.length);
<span class="hljs-keyword">const</span> quote = quotes[random].quote
<span class="hljs-keyword">const</span> author = quotes[random].author
</code></pre>
<p>And just like we did with the welcome message, we just return the quote and author instead of a string message:</p>
<pre><code class="lang-javascript"><span class="hljs-string">`:zap: <span class="hljs-subst">${quote}</span> - *<span class="hljs-subst">${author}</span>*`</span>
</code></pre>
<p>Let's test this:</p>
<p>Type <code>@inspirenuggets inspire me</code></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture20.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Yayyy! It worked!</p>
<p>PS: You can always change the emoji type for every request. If you noticed I changed the <code>inspireMe()</code> to <code>:male-technologist:</code></p>
<p><strong>randomJoke()</strong></p>
<p>We're getting the jokes from the Chuck Norris API from this endpoint <code>https://api.chucknorris.io/jokes/random</code>.</p>
<pre><code class="lang-json">{
<span class="hljs-attr">"categories"</span>: [],
<span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2016-05-01 10:51:41.584544"</span>,
<span class="hljs-attr">"icon_url"</span>: <span class="hljs-string">"https://assets.chucknorris.host/img/avatar/chuck-norris.png"</span>,
<span class="hljs-attr">"id"</span>: <span class="hljs-string">"6vUvusBeSVqdsU9C5-ZJZw"</span>,
<span class="hljs-attr">"updated_at"</span>: <span class="hljs-string">"2016-05-01 10:51:41.584544"</span>,
<span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://api.chucknorris.io/jokes/6vUvusBeSVqdsU9C5-ZJZw"</span>,
<span class="hljs-attr">"value"</span>: <span class="hljs-string">"Chuck Norris once choked a wildcat to death with his sphincter muscle."</span>
}
</code></pre>
<p>This is a real API that returns a random joke on every request, so we don't have to do <code>Math.floor()</code> again.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">randomJoke</span>(<span class="hljs-params"></span>) </span>{
    axios.get(<span class="hljs-string">'https://api.chucknorris.io/jokes/random'</span>)
      .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
            <span class="hljs-keyword">const</span> joke = res.data.value;

            <span class="hljs-keyword">const</span> params = {
                <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':smile:'</span>
            }

            bot.postMessageToChannel(
                <span class="hljs-string">'random'</span>,
                <span class="hljs-string">`:zap: <span class="hljs-subst">${joke}</span>`</span>,
                params
            );

      })
}
</code></pre>
<p>By now, you should understand how this works already. Make a post with the channel name, message and params.</p>
<p><strong>runHelp()</strong></p>
<p>This is similar to our welcome message: we just want to return a custom text when the user adds <strong>help</strong> to the request.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">runHelp</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> params = {
        <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':question:'</span>
    }

    bot.postMessageToChannel(
        <span class="hljs-string">'random'</span>,
        <span class="hljs-string">`Type *@inspirenuggets* with *inspire me* to get an inspiring techie quote, *random joke* to get a Chuck Norris random joke and *help* to get this instruction again`</span>,
        params
    );
}
</code></pre>
<p>Now let's test all three commands:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture2-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Everything works fine now, congratulations!!!! You just built your SlackBot.</p>
<hr>
<p>There are an endless number of possibilities of Bots you can build with this to automate your own work or teamwork.</p>
<p>You can build a bot that:</p>
<ul>
<li><p>Fetches your tasks from somewhere and reminds you when you type <code>hey what next</code>,</p>
</li>
<li><p>Welcomes every user to your workspace (I built this during one of the <a target="_blank" href="https://hng.tech/">HNG Internship's</a>),</p>
</li>
<li><p>Gives you football matches updates while you're working,</p>
</li>
<li><p>Tells your team when you hit a milestone in number of registered users,</p>
</li>
</ul>
<p>and many more...</p>
<p>It's just about having somewhere to get the data from, and some basic iteration skills and the <code>bot.postMessageToChannel()</code> method.</p>
<p>Automation is one thing we should learn as developers. We have a lot to do, so we should automate the simpler tasks so we have time for the more difficult ones. I hope with this you can automate your tasks and I look forward to the creative ideas you'll bring to life.</p>
<hr>
<h1 id="heading-final-code">Final Code</h1>
<p>Here's our final <code>index.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> SlackBot = <span class="hljs-built_in">require</span>(<span class="hljs-string">'slackbots'</span>);
<span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">'axios'</span>)
<span class="hljs-keyword">const</span> dotenv = <span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>)

dotenv.config()

<span class="hljs-keyword">const</span> bot = <span class="hljs-keyword">new</span> SlackBot({
    <span class="hljs-attr">token</span>: <span class="hljs-string">`<span class="hljs-subst">${process.env.BOT_TOKEN}</span>`</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">'inspirenuggets'</span>
})

<span class="hljs-comment">// Start Handler</span>
bot.on(<span class="hljs-string">'start'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> params = {
        <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':robot_face:'</span>
    }

    bot.postMessageToChannel(
        <span class="hljs-string">'random'</span>,
        <span class="hljs-string">'Get inspired while working with @inspirenuggets'</span>,
        params
    );
})

<span class="hljs-comment">// Error Handler</span>
bot.on(<span class="hljs-string">'error'</span>, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(err);
})

<span class="hljs-comment">// Message Handler</span>
bot.on(<span class="hljs-string">'message'</span>, <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span>(data.type !== <span class="hljs-string">'message'</span>) {
        <span class="hljs-keyword">return</span>;
    }
    handleMessage(data.text);
})

<span class="hljs-comment">// Response Handler</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleMessage</span>(<span class="hljs-params">message</span>) </span>{
    <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' inspire me'</span>)) {
        inspireMe()
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' random joke'</span>)) {
        randomJoke()
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' help'</span>)) {
        runHelp()
    }
}

<span class="hljs-comment">// inspire Me</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">inspireMe</span>(<span class="hljs-params"></span>) </span>{
    axios.get(<span class="hljs-string">'https://raw.githubusercontent.com/BolajiAyodeji/inspireNuggets/master/src/quotes.json'</span>)
      .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
            <span class="hljs-keyword">const</span> quotes = res.data;
            <span class="hljs-keyword">const</span> random = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * quotes.length);
            <span class="hljs-keyword">const</span> quote = quotes[random].quote
            <span class="hljs-keyword">const</span> author = quotes[random].author

            <span class="hljs-keyword">const</span> params = {
                <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':male-technologist:'</span>
            }

            bot.postMessageToChannel(
                <span class="hljs-string">'random'</span>,
                <span class="hljs-string">`:zap: <span class="hljs-subst">${quote}</span> - *<span class="hljs-subst">${author}</span>*`</span>,
                params
            );

      })
}

<span class="hljs-comment">// Random Joke</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">randomJoke</span>(<span class="hljs-params"></span>) </span>{
    axios.get(<span class="hljs-string">'https://api.chucknorris.io/jokes/random'</span>)
      .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
            <span class="hljs-keyword">const</span> joke = res.data.value;

            <span class="hljs-keyword">const</span> params = {
                <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':smile:'</span>
            }

            bot.postMessageToChannel(
                <span class="hljs-string">'random'</span>,
                <span class="hljs-string">`:zap: <span class="hljs-subst">${joke}</span>`</span>,
                params
            );

      })
}

<span class="hljs-comment">// Show Help</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">runHelp</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> params = {
        <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':question:'</span>
    }

    bot.postMessageToChannel(
        <span class="hljs-string">'random'</span>,
        <span class="hljs-string">`Type *@inspirenuggets* with *inspire me* to get an inspiring techie quote, *random joke* to get a Chuck Norris random joke and *help* to get this instruction again`</span>,
        params
    );
}
</code></pre>
<h1 id="heading-what-next">What Next?</h1>
<p>Our bot only runs in development now, and to use it we always have to <code>npm start</code>.</p>
<p>This isn't cool, right? We'll want to host it somewhere it can run every time. In my next article, I'll show you how to host this on either <a target="_blank" href="https://herokuapp.com/">Heroku</a>, <a target="_blank" href="https://zeit.co/">Zeit</a> or <a target="_blank" href="https://netlify.com">Netlify</a> and publish it to the Slack Apps store so anyone around the world can use it.<br>Also, don't forget to add this in your <code>.gitignore</code> before pushing to GitHub:</p>
<pre><code class="lang-python">
/.env
/node_modules
</code></pre>
<blockquote>
<p><strong>Subscribe to my</strong> <a target="_blank" href="https://tinyletter.com/bolajiayodeji/"><strong>newsletter</strong></a> <strong>to get updated.</strong></p>
</blockquote>
<h1 id="heading-useful-resources">Useful Resources</h1>
<ul>
<li><p><a target="_blank" href="https://api.slack.com/">Slack API</a></p>
</li>
<li><p><a target="_blank" href="https://api.slack.com/#read_the_docs">Slack API Docs</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/slackapi/node-slack-sdk">SlackBot.js</a></p>
</li>
<li><p><a target="_blank" href="https://slack.com/intl/en-in/apps">Slack Apps</a></p>
</li>
<li><p><a target="_blank" href="https://api.slack.com/docs/slack-apps-guidelines">Slack Apps Guidelines</a></p>
</li>
<li><p><a target="_blank" href="https://api.slack.com/start/overview">An introduction to Slack apps</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/BolajiAyodeji/inspireNuggets">inspireNuggets</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/BolajiAyodeji/inspireNuggetsSlackBot">inspireNuggetsSlackBot</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn Serverless by Building your own Slack App ]]>
                </title>
                <description>
                    <![CDATA[ By Lekha Surasani Serverless architecture is the industry's latest buzzword and many of the largest tech companies have begun to embrace it.  In this article, we'll learn what it is and why you should use it. We'll also set up AWS, create our serverl... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/make-a-serverless-slack-app/</link>
                <guid isPermaLink="false">66d46018787a2a3b05af43d4</guid>
                
                    <category>
                        <![CDATA[ aws lambda ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 08 Aug 2019 11:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/08/serverless-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Lekha Surasani</p>
<p>Serverless architecture is the industry's latest buzzword and many of the largest tech companies have begun to embrace it. </p>
<p>In this article, we'll learn what it is and why you should use it. We'll also set up AWS, create our serverless app, and create a slack app!</p>
<h2 id="heading-what-is-serverless">What is Serverless?</h2>
<p>Serverless is a cloud computing paradigm in which the developer no longer has to worry about maintaining a server – they just focus on the code. </p>
<p>Cloud providers, such as AWS or Azure, are now responsible for executing code and maintaining servers by dynamically allocating their resources. A variety of events can trigger code execution, including cron jobs, http requests, or database events. </p>
<p>The code that developers send to the cloud is usually just a function so, many times, serverless architecture is implemented using Functions-as-a-Service, or FaaS. The major cloud providers provide frameworks for FaaS, such as AWS Lambda and Azure Functions.</p>
<h2 id="heading-why-serverless">Why Serverless?</h2>
<p>Not only does serverless allow developers to just focus on code, but it has many other benefits as well. </p>
<p>Since cloud providers are now responsible for executing code and dynamically allocate resources based on event triggers, you typically only pay per request, or when your code is being executed. </p>
<p>Additionally, since cloud providers are handling your servers, you don't have to worry about scaling up – the cloud provider will handle it. This makes serverless apps lower cost, easier to maintain, and easier to scale.</p>
<hr>
<h2 id="heading-setting-up-aws-lambda">Setting up AWS Lambda</h2>
<p>For this tutorial, I will be using AWS Lambda, so first, we'll create an <a target="_blank" href="https://aws.amazon.com/">AWS account</a>. I find AWS's UI hard to understand and difficult to navigate, so I will be adding screenshots for each step.</p>
<p>Once you log in, you should see this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-17.png" alt="Image" width="600" height="400" loading="lazy">
<em>Main screen</em></p>
<p>Next, we'll set up an IAM user. An <a target="_blank" href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html">IAM</a> (Identity and Access Management) user interacts with AWS and its resources on your behalf. This allows you to create different IAM users with different permissions and purposes, without compromising the security of your root user account.</p>
<p>Click on the "services" tab at the top of the page, and type "IAM" into the bar:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-27.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click on the first result, and you'll see, on the left-hand sidebar, that you're at the dashboard. Click on the "Users" option to get to create our new IAM user. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-28.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click on the "Add user" button to create a new user. Fill in the details as follows:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-29.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can name your user anything you'd like, but I went with <code>serverless-admin</code>. Be sure that your user has "Programmatic access" to AWS, <strong>not</strong> "AWS Management Console Access". You'd use the latter for teammates, or other <em>humans</em> who need access to AWS. We just need this user to interact with AWS Lambda, so we can just give them programmatic access. </p>
<p>For permissions, I've chosen to attach existing policies since I don't have any groups, and I don't have any existing users that I want to copy permissions for. In this example, I will create the user with Administrator access since it's just for a personal project; however, if you were to use a serverless app in an actual production environment, your IAM user should be scoped to only access Lambda-necessary parts of AWS. (Instructions can be found <a target="_blank" href="https://serverless.com/blog/abcs-of-iam-permissions/">here</a>).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-58.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I didn't add any tags and created the user. It's vital to save the information given to you on the next screen - the Access ID and Secret Access Key.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-04-IAM-Management-Console.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Don't leave this screen without copying down both! You won't be able to see the Secret access key again after this screen.</p>
<p>Finally, we'll add these credentials to command line AWS. Use this <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html">guide</a> to get aws cli setup.</p>
<p>Make sure you have it installed by running <code>aws --version</code>. You should see something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-2.02.27-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then run <code>aws configure</code> and fill in the prompts:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-5.42.53-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I have the default region as <code>us-east-2</code> already set up, but you can use <a target="_blank" href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html">this</a> to determine what your region is.</p>
<p>To make sure that you have your credentials set up correctly, you can run <code>cat ~/.aws/credentials</code> in your terminal.</p>
<p>If you want to configure a profile other than your default, you can run the command as follows: <code>aws configure --profile [profile name]</code>.</p>
<p>If you had trouble following the steps, you can also check out <a target="_blank" href="https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html">AWS's documentation</a>.</p>
<hr>
<h2 id="heading-set-up-serverless">Set up serverless</h2>
<p>Go to your terminal and install the <code>serverless</code> package globally using <code>npm</code>: <code>npm i -g serverless</code>. (<a target="_blank" href="https://serverless.com/">More info on serverless here</a>)<br>and your terminal should look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-1.55.12-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, navigate to the directory where you want to create the app, then run <code>serverless</code> and follow the prompts:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-04-at-5.55.03-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>For this application, we'll be using Node.js. You can name your app anything you want, but I've called mine <code>exampleSlackApp</code>.</p>
<p>Open your favorite code editor to the contents in <code>exampleSlackApp</code> (or whatever you've called your application).</p>
<p>First, we'll take a look at <code>serverless.yml</code>. You'll see there's a lot of commented code here describing the different options you can use in the file. Definitely give it a read, but I've deleted it down to just:</p>
<pre><code>service: exampleslackapp

<span class="hljs-attr">provider</span>:
  name: aws
  <span class="hljs-attr">runtime</span>: nodejs10.x
  <span class="hljs-attr">region</span>: us-east<span class="hljs-number">-2</span>

<span class="hljs-attr">functions</span>:
  hello:
    handler: handler.hello
</code></pre><p> I've included <code>region</code> since the default is <code>us-east-1</code> but my aws profile is configured for <code>us-east-2</code>.</p>
<p>Let's deploy what we already have by running <code>serverless deploy</code> in the directory of the app that <code>serverless</code> just created for us. The output should look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-05-at-12.07.10-AM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And if you run <code>serverless invoke -f hello</code> in your terminal, it'll run the app, and you should see:</p>
<pre><code>{
    <span class="hljs-string">"statusCode"</span>: <span class="hljs-number">200</span>,
    <span class="hljs-string">"body"</span>: <span class="hljs-string">"{\n  \"message\": \"Go Serverless v1.0! Your function executed successfully!\",\n  \"input\": {}\n}"</span>
}
</code></pre><p>For further proof that our slack app is live, you can head back to AWS console. Go to the services dropdown, search for "Lambda", and click on the first option ("Run code without thinking about servers").</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-32.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>And here's your app!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-33.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, we'll explore actually using serverless by building our slack app. Our slack app will post a random <a target="_blank" href="https://en.wikipedia.org/wiki/Ron_Swanson">Ron Swanson</a> quote to slack using a <a target="_blank" href="https://api.slack.com/slash-commands">slash command</a> like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-07-at-10.23.40-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The following steps don't necessarily have to be done in the order that I've done them, so if you want to skip around, feel free!</p>
<hr>
<h2 id="heading-adding-the-api-to-our-code">Adding the API to our code</h2>
<p>I'm using <a target="_blank" href="https://github.com/jamesseanwright/ron-swanson-quotes#ron-swanson-quotes-api?ref=public-apis">this API</a> to generate Ron Swanson quotes since the docs are fairly simple (and of course, it's free). To see how requests are make and what gets returned, you can just put this URL in your browser:</p>
<p><a target="_blank" href="https://ron-swanson-quotes.herokuapp.com/v2/quotes"><code>https://ron-swanson-quotes.herokuapp.com/v2/quotes</code></a></p>
<p>You should see something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-59.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>So, we can take our initial function and modify it as such:</p>
<pre><code><span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  getRon();
};
</code></pre><p>and <code>getRon</code> looks like:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRon</span>(<span class="hljs-params"></span>) </span>{
  request(<span class="hljs-string">'https://ron-swanson-quotes.herokuapp.com/v2/quotes'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
  })
}
</code></pre><p>Now, let's check if it works. To test this code locally, in your terminal: <code>serverless invoke local -f hello</code>. Your output should look something like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-07-at-9.41.53-PM.png" alt="Image" width="600" height="400" loading="lazy">
<em>Spoiler: There was a wrong way to consume alcohol</em></p>
<p><code>serverless invoke -f hello</code> would run the code that you've deployed, as we saw in previous sections. <code>serverless invoke local -f hello</code>, however, runs your local code, so it's useful for testing. Go ahead and deploy using <code>serverless deploy</code>!</p>
<hr>
<h2 id="heading-create-your-slack-app">Create your Slack App</h2>
<p>To create your slack app, follow this <a target="_blank" href="https://api.slack.com/apps?new_app=1">link</a>. It'll make you sign into a slack workspace first, so be sure you're a part of one that you can add this app to. I've created a testing one for my purposes. You'll be prompted with this modal. You can fill in whatever you want, but here's what I have as an example:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-61.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>From there, you'll be taken to the homepage for your app. You should definitely explore these pages and the options. For example, I've added the following customization to my app:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-62.png" alt="Image" width="600" height="400" loading="lazy">
<em>Display information can be found from the "Basic Information" tab on the app</em></p>
<p>Next, we need to add some permissions to the app:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Slack-API-Applications-lekha_test-Slack.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>To get an OAuth Access Token, you have to add some scope and permissions, which you can do by scrolling down:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-64.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I've added "Modify your public channels" so that the bot could write to a channel, "Send messages as Ron Swanson" so when the message gets posted, it looks like a user called Ron Swanson is posting the message, and slash commands so the user can "request" a quote as shown in the screenshot at the beginning of the article. After you save the changes, you should be able to scroll back up to OAuths &amp; Permissions to see:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-65.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click the button to Install App to Workspace, and you'll have an OAuth Access Token! We'll come back to this in a second, so either copy it down or remember it's in this spot.</p>
<hr>
<h2 id="heading-connect-code-and-slack-app">Connect Code and Slack App</h2>
<p>In AWS Lambda, find your slack app function. Your Function Code section should show our updated code with the call to our Ron Swanson API (if it does not, go back to your terminal and run <code>serverless deploy</code>). </p>
<p>Scroll below that to the section that says "<a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/env_variables.html">Environment Variables</a>", and put your Slack OAuth Access Token here (you can name the key whatever you'd like):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Lambda-Management-Console.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Let's go back to our code and add Slack into our function. At the top of our file, we can declare a <code>const</code> with our new OAuth Token: </p>
<p><code>const SLACK_OAUTH_TOKEN = process.env.OAUTH_TOKEN</code>. </p>
<p><code>process.env</code> just grabs our environment variables (<a target="_blank" href="https://nodejs.org/dist/latest-v8.x/docs/api/process.html#process_process_env">additional reading</a>). Next, let's take a look at the <a target="_blank" href="https://api.slack.com/methods/chat.postMessage">Slack API</a> to figure out how to post a message to a channel.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-67.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-76.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The two pictures above I've taken from the API are the most relevant to us. So, to make this API request, I'll use <code>request</code> by passing in an object called <code>options</code>:</p>
<pre><code>  <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'https://slack.com/api/chat.postMessage'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'application/json'</span>,
    },
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">form</span>: {
      <span class="hljs-attr">token</span>: SLACK_OAUTH_TOKEN,
      <span class="hljs-attr">channel</span>: <span class="hljs-string">'general'</span>, <span class="hljs-comment">// hard coding for now</span>
      <span class="hljs-attr">text</span>: <span class="hljs-string">'I am here'</span>,
    }
  }
</code></pre><p>and we can make the request:</p>
<pre><code>  request(options, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
  })
</code></pre><p>Finally, I'll wrap the whole thing in a function:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postRon</span>(<span class="hljs-params">quote</span>) </span>{
  <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'https://slack.com/api/chat.postMessage'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'application/json'</span>,
    },
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">form</span>: {
      <span class="hljs-attr">token</span>: SLACK_OAUTH_TOKEN,
      <span class="hljs-attr">channel</span>: <span class="hljs-string">'general'</span>,
      <span class="hljs-attr">text</span>: quote,
    }
  }

  request(options, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
  })
}
</code></pre><p>and we can call it from <code>getRon</code> like this:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRon</span>(<span class="hljs-params"></span>) </span>{
  request(<span class="hljs-string">'https://ron-swanson-quotes.herokuapp.com/v2/quotes'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
    postRon(body.substring(<span class="hljs-number">2</span>, body.length - <span class="hljs-number">2</span>)) <span class="hljs-comment">// here for parsing, remove if you want to see how/why I did it</span>
  })
}
</code></pre><p>So our code should all in all look like this:</p>
<pre><code><span class="hljs-meta">'use strict'</span>;
<span class="hljs-keyword">let</span> request = <span class="hljs-built_in">require</span>(<span class="hljs-string">'request'</span>);

<span class="hljs-keyword">const</span> SLACK_OAUTH_TOKEN = process.env.OAUTH_TOKEN

<span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
  getRon();
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRon</span>(<span class="hljs-params"></span>) </span>{
  request(<span class="hljs-string">'https://ron-swanson-quotes.herokuapp.com/v2/quotes'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
    postRon(body.substring(<span class="hljs-number">2</span>, body.length - <span class="hljs-number">2</span>))
  })
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postRon</span>(<span class="hljs-params">quote</span>) </span>{
  <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'https://slack.com/api/chat.postMessage'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'application/json'</span>,
    },
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">form</span>: {
      <span class="hljs-attr">token</span>: SLACK_OAUTH_TOKEN,
      <span class="hljs-attr">channel</span>: <span class="hljs-string">'general'</span>,
      <span class="hljs-attr">text</span>: quote,
    }
  }

  request(options, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
  })
}
</code></pre><p>Now let's test! Unfortunately, our environment variable in AWS Lambda isn't available to us when we run <code>serverless invoke local -f hello</code>. There are a few ways you can approach this, but for our purposes, you can just replace the value for <code>SLACK_OAUTH_TOKEN</code> with your actual OAuth Token (make sure it's a string). But be sure you switch it back before you push it up to version control! </p>
<p>Run <code>serverless invoke local -f hello</code>, and hopefully you should see a message like this in your #general channel:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-69.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Please note that I put down my channel name as 'general' since it's my test workspace; however, if you're in an actual workspace, you should create a separate channel for testing apps, and put the message there instead while you're testing.</em></p>
<p>And in your terminal, you should see something like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screen-Shot-2019-08-07-at-10.48.38-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If that works, go ahead and deploy it using <code>serverless deploy</code>. If it does not, the best way to debug this is to adjust code and run <code>serverless invoke local -f hello</code>.</p>
<hr>
<h2 id="heading-adding-slash-command">Adding slash command</h2>
<p>The last and final part is adding a slash command! Go back to your function's home page in AWS Lambda and look for the button that says "Add trigger":</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-70.png" alt="Image" width="600" height="400" loading="lazy">
<em>We're going to add an API Gateway (as I already have).</em></p>
<p>Click on the button to get to the "Add trigger" page, and select "API Gateway" from the list:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-71.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p> I've filled in the information based on defaults mostly:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-72.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I've also left this API open for use – however, if you're using this in production,  you should discuss what standard protocol would be with your team. "Add" the API, and you should receive an API endpoint. Hold on to this, because we'll need it for the next step. </p>
<p>Let's switch back over to our slack app and add a slash command:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-73.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click on "Create New Command" and it should pop up with a new window to create a command. Here's how I filled mine out:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Slack-API-Applications-lekha_test-Slack-1-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can enter anything you want for "command" and "short description" but for "request URL", you should put your API endpoint.</p>
<p>Finally, we'll go back to our code to make some final adjustments. If you try to use the slash command, you should receive some kind of error back – this is because slack expects a response and AWS expects you to give a response when the endpoint is hit. So, we'll change our function to allow a <code>callback</code> (<a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html">for reference</a>):</p>
<pre><code><span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event,context,callback</span>) =&gt;</span> {
  getRon(callback);
};
</code></pre><p>and then we'll change <code>getRon</code> to do something with the <code>callback</code>:</p>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRon</span>(<span class="hljs-params">callback</span>) </span>{
  request(<span class="hljs-string">'https://ron-swanson-quotes.herokuapp.com/v2/quotes'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
    callback(<span class="hljs-literal">null</span>, SUCCESS_RESPONSE)
    postRon(body.substring(<span class="hljs-number">2</span>, body.length - <span class="hljs-number">2</span>))
  })
}
</code></pre><p>where <code>SUCCESS_RESPONSE</code> is at the top of the file:</p>
<pre><code><span class="hljs-keyword">const</span> SUCCESS_RESPONSE = {
  <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
  <span class="hljs-attr">body</span>: <span class="hljs-literal">null</span>
}
</code></pre><p>You can put the callback here or in <code>postRon</code> – it just depends on what your purposes are with the callback. </p>
<p>Our code at this point now looks something like:</p>
<pre><code><span class="hljs-meta">'use strict'</span>;
<span class="hljs-keyword">let</span> request = <span class="hljs-built_in">require</span>(<span class="hljs-string">'request'</span>);

<span class="hljs-keyword">const</span> SLACK_OAUTH_TOKEN = OAUTH_TOKEN

<span class="hljs-keyword">const</span> SUCCESS_RESPONSE = {
  <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,
  <span class="hljs-attr">body</span>: <span class="hljs-literal">null</span>
}

<span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event,context,callback</span>) =&gt;</span> {
  getRon(callback);
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRon</span>(<span class="hljs-params">callback</span>) </span>{
  request(<span class="hljs-string">'https://ron-swanson-quotes.herokuapp.com/v2/quotes'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
    callback(<span class="hljs-literal">null</span>, SUCCESS_RESPONSE)
    postRon(body.substring(<span class="hljs-number">2</span>, body.length - <span class="hljs-number">2</span>))
  })
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postRon</span>(<span class="hljs-params">quote</span>) </span>{
  <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'https://slack.com/api/chat.postMessage'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'application/json'</span>,
    },
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">form</span>: {
      <span class="hljs-attr">token</span>: SLACK_OAUTH_TOKEN,
      <span class="hljs-attr">channel</span>: <span class="hljs-string">'general'</span>,
      <span class="hljs-attr">text</span>: quote,
    }
  }

  request(options, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, resp, body</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error:'</span>, err)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'statusCode:'</span>, resp &amp;&amp; resp.statusCode)
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'body'</span>, body)
  })
}
</code></pre><p>You should be able to use the <code>/ron</code> command in slack now and get a Ron Swanson quote back. If you don't, you can use Cloudwatch logs to see what went wrong:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Screenshot_2019-08-07-Lambda-Management-Console-1-.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The way our code works now, we've hardcoded in the channel name. But, what we actually want is for the quote to get posted in the message where you used <code>/ron</code>. </p>
<p>So, we can now use the <code>event</code> portion of our function. </p>
<pre><code><span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event,context,callback</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(event)
  getRon(callback);
};
</code></pre><p>Use <code>/ron</code> to run the function, and then check your Cloudwatch logs to see what gets logged to the console (you may need to refresh). Check on the most recent logs and you should see something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-74.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The first item in this list (where it says "resource", "path", etc.) is the event, so if you expand that, you'll see a long list of things, but what we're looking for is 'body' all the way down at the bottom:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/image-75.png" alt="Image" width="600" height="400" loading="lazy">
<em>where's waldo: spot the param edition</em></p>
<p>Body is a string with some relevant information in it, one of them being "channel_id". We can use channel_id (or channel_name) and pass it into the function that creates our slack message. For your convenience, I've already parsed this string: <code>event.body.split("&amp;")[3].split("=")[1]</code> should give you the channel_id. I hardcoded in which entry (3) the channel_id was for simplicity.</p>
<p>Now, we can alter our code to save that string as a variable:</p>
<p><code>let channel = 'general'</code> (as our fallback)</p>
<pre><code><span class="hljs-built_in">module</span>.exports.hello = <span class="hljs-function">(<span class="hljs-params">event,context,callback</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(event)
  channel = event.body.split(<span class="hljs-string">"&amp;"</span>)[<span class="hljs-number">3</span>].split(<span class="hljs-string">"="</span>)[<span class="hljs-number">1</span>]
  <span class="hljs-built_in">console</span>.log(context)
  getGoat(callback);
};
</code></pre><p>and in <code>postRon</code>:</p>
<pre><code>  <span class="hljs-keyword">let</span> options = {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'https://slack.com/api/chat.postMessage'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'Accept'</span>: <span class="hljs-string">'application/json'</span>,
    },
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">form</span>: {
      <span class="hljs-attr">token</span>: SLACK_OAUTH_TOKEN,
      <span class="hljs-attr">channel</span>: channel,
      <span class="hljs-attr">text</span>: quote,
    }
  }
</code></pre><p>Finally, if you use a slack command in any channel in your workspace, you should be able to see a Ron Swanson quote pop up! If not, as I mentioned before, the most common tools I use to debug serverless apps are <code>serverless invoke local -f &lt;function name&gt;</code> and Cloudwatch logs.</p>
<hr>
<p>Hopefully you were successfully able to create a functioning Slack application! I've included resources and background reading dispersed throughout the article and I'm happy to answer any questions you may have!</p>
<p><em>Final Repo with code:</em> <a target="_blank" href="https://github.com/lsurasani/ron-swanson-slack-app/">https://github.com/lsurasani/ron-swanson-slack-app/</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to build a basic slackbot: a  beginner’s guide ]]>
                </title>
                <description>
                    <![CDATA[ By Vishwa Shah Update: code and tutorial updated on June 28 to reflect Slack API changes. Slackbots: Why use them? Before we get into the tutorial part of this post, let’s take a look at why this can be a worthy project and tool. Slack is an increasi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-basic-slackbot-a-beginners-guide-6b40507db5c5/</link>
                <guid isPermaLink="false">66c34f4ba124e2df05195f82</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sun, 06 Jan 2019 20:29:51 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*h89l_KJR8w2NrzQXtPCAmw.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Vishwa Shah</p>
<p><strong>Update: code and tutorial updated on June 28 to reflect Slack API changes</strong>.</p>
<h1 id="heading-slackbots-why-use-them">Slackbots: Why use them?</h1>
<p>Before we get into the tutorial part of this post, let’s take a look at why this can be a worthy project and tool.</p>
<p><a target="_blank" href="http://slack.com/">Slack</a> is an increasingly popular tool for team-wide communication. It’s grown to include plugins for other widely used project management tools, like JIRA, Google Drive, and the likes. Any slack user knows — the more you can do from within the conversation, the better.</p>
<p>Common uses for a slackbot range from a simple notifier for when a task is complete (like a test build, or when your lunch is ready) to interactive, button-based bots that execute commands at the user’s will. You can build polling mechanisms, conversational bots, and more.</p>
<h1 id="heading-setting-up-a-python-programming-environment">Setting up a python programming environment</h1>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/1_p65lij8MGz7AUNRl9D3sNw.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you’re a windows user and you haven’t used python before, you’ll need to <a target="_blank" href="https://docs.python.org/3/using/windows.html">install</a> it. Linux/Mac users: Unix comes with python!</p>
<p>Once installed, fire up your terminal and type <code>python</code> or <code>python3</code> (if you have multiple installations) to make sure it works and is there.</p>
<p>Also check to see you have a good text editor for code: <a target="_blank" href="https://www.sublimetext.com/3">sublime</a> and <a target="_blank" href="https://flight-manual.atom.io/getting-started/sections/installing-atom/">atom</a> are great choices.</p>
<p>Optional: It might also be useful to work in a virtual environment — it’s good practice for when you have a lot of dependencies.</p>
<pre><code>pip install virtualenv
virtualenv tutorial
source tutorial/bin/activate
</code></pre><p><img src="https://www.freecodecamp.org/news/content/images/2020/06/1_QHX3UYi6NFRe_xteZ9qiYQ.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You should also fork the <a target="_blank" href="https://github.com/vishwa35/slackbot-tutorial">tutorial GitHub repo</a> and clone to your local machine, as we’ll be using that code as a framework for this tutorial.</p>
<p>To do this, go to the <a target="_blank" href="https://github.com/vishwa35/slackbot-tutorial">repo</a> and click <code>Fork</code> on the top right. The forked repo should be <strong>/slackbot-tutorial</strong>. Hit the green <code>Clone or download</code> button on the right under the stats bar, and copy the url. Return to the terminal to clone the repository:</p>
<pre><code>cd Desktop/
git clone https:<span class="hljs-comment">//github.com/yourusername/slackbot-tutorial.git</span>
cd slackbot-tutorial/
sublime . (or open your text editor and open <span class="hljs-built_in">this</span> directory)
</code></pre><h1 id="heading-slack-apps">Slack Apps</h1>
<p>There are two ways to go about creating your slackbot: standalone bots, or Slack apps. Apps allow a wider range of functionality going forward, and is Slack’s recommended route for creating a bot user.</p>
<p>Go to <a target="_blank" href="https://api.slack.com/apps">https://api.slack.com/apps</a> and hit <code>Create New App</code> on the top right. Give it a name and pick a workspace where you can create a channel to test your bot in. You can always reconfigure your bot for another workspace later, or even post it to the Slack App Directory.</p>
<p>I recommend making a #test channel in a workspace you create just for development purposes, or one that has relatively few users who wouldn’t mind you testing something out there.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/1_aHE2Te70SwllhMxUE5Tgpg.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The first thing you’ll want to do is get the bot token. When you get to the above page, click Bots. Add some scopes; these determine what permissions your app’s bot user will have. To start, <a target="_blank" href="https://api.slack.com/scopes/chat:write"><strong>chat:write</strong></a> and <a target="_blank" href="https://api.slack.com/scopes/im:write"><strong>im:write</strong></a> are probably enough.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/1_881iK1n-kUuZVXagvfS5qg.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now, to actually get your tokens, you’ll want to go to <code>OAuth &amp; Permissions</code> on the left sidebar.</p>
<p>Here, you’ll be able to <code>Install the App to the Workspace</code> and generate the necessary tokens. As a rule of thumb, <strong>bot tokens</strong> start with <code>xoxb-.</code></p>
<p>You’ll also want the s<strong>igning secret</strong>, which is located under Basic Information &gt; App Credentials.</p>
<h2 id="heading-acting-as-your-bot">Acting as your Bot</h2>
<p>Now you have the credentials necessary to make API calls and act as your bot. To test this out, fire up a terminal and run this (with the correct token and channel name):</p>
<pre><code>curl -X POST \
     -H <span class="hljs-string">'Authorization: Bearer xoxb-your-token'</span> \
     -H <span class="hljs-string">'Content-type: application/json;charset=utf-8'</span> \
    --data <span class="hljs-string">'{"channel":"#test","text":"Hello, Slack!"}'</span> \
<span class="hljs-attr">https</span>:<span class="hljs-comment">//slack.com/api/chat.postMessage</span>
</code></pre><p>If you go to that channel in your slack workspace, you should now see a message from your bot! You just made an HTTP POST request — asked a server to post a message somewhere.</p>
<h1 id="heading-programming-the-bot">Programming the Bot</h1>
<p>We want to do the above programatically. There are a few different ways you can set up a slackbot. I’ll cover the following:</p>
<ul>
<li>Triggered periodically (on a schedule) to say something</li>
<li>/slash commands</li>
</ul>
<p>The second requires a server running, while the first does not.</p>
<h2 id="heading-scheduled-messages">Scheduled Messages</h2>
<p>Let’s say you want to periodically send a message somewhere — maybe every Monday morning. Go to the text editor where you opened up <code>slackbot-tutorial</code>.</p>
<p>You should see a file <code>scheduled.py</code>. Take a look: <code>sendMessage</code> is a function that fires off the API call to slack and posts a message. At the bottom, you’ll see the main method: what executes when you run the script. Here, you’ll see a few things to note:</p>
<ul>
<li><code>SLACK_BOT_TOKEN</code> is pulled from <code>os.environ['SLACK_BOT_TOKEN']</code> — how? Run <code>export SLACK_BOT_TOKEN="xoxb-your-token"</code> in your terminal to set this variable.</li>
<li>a scheduler is used here, and there’s an infinite loop that checks for events on the scheduler. By default here, I’ve scheduled the <code>sendMessage</code> function to be called every minute.</li>
</ul>
<p>To test this out, go back to the terminal where you’re in the <code>slackbot-tutorial</code> directory and run</p>
<pre><code><span class="hljs-keyword">export</span> SLACK_BOT_TOKEN=<span class="hljs-string">"xoxb-your-token"</span>
python scheduled.py
</code></pre><p>You should see the log messages print. Make sure you’ve changed <code>**channel=#test**</code> in the code to your test channel name (if different) and added your bot (in the slack channel, type <code>/invite @botname</code>. Let it run for a couple minutes and watch the messages show up on Slack!</p>
<p>This is, of course, a super basic implementation of a scheduled message sender — you can actually do this just with slackbot <code>/remind #test “Hello, Slack!” every Monday at 9am</code>.</p>
<p>The <strong>true power</strong> here is that you can substitute in any function for <code>sendMessage</code>, leveraging the power of interfacing with external services through APIs, doing math, etc and then constructing a message to post.</p>
<h2 id="heading-slash-commands">Slash Commands</h2>
<p>This one requires a little more setup — go back to your <a target="_blank" href="http://api.slack.com/apps">app settings</a> &gt; Slash Commands. Create a new slash command: for example, <code>/test</code>. For the request URL, you’ll need to either deploy this web server (I use Heroku), or run a local <code>ngrok</code> instance to test it. The latter will run it locally, and is best for testing. You can <code>brew install ngrok</code> or get it from <a target="_blank" href="https://ngrok.com/download">here</a>.</p>
<p>In the starter code repo, look for <code>slashCommand.py</code> to start understanding this method. To start the server, run <code>python server.py</code>. The Request URL to put in Slack will be given by your <code>ngrok</code> instance and the <code>@app.route</code> in your code — it would be something like <a target="_blank" href="http://a8787d2fea3b.ngrok.io/">http://a1234b5cde6f.ngrok.io</a>/<strong>slack/test</strong> (the bold part comes from the route defined in the code). You should be able to test the slash commands in your Slack workspace. From the tutorial code, try <code>/test</code>.</p>
<h1 id="heading-moving-forward">Moving Forward</h1>
<p>Now you have a very basic slackbot that either operates on a command or runs every so often. Be creative with how you use it! Think about what else you can link this skeleton to to make it more useful.</p>
<h2 id="heading-other-ways-your-bot-might-respond">Other ways your bot might respond</h2>
<ol>
<li>Actions/responses could be triggered by mentions or certain phrases. This requires running a server and listening the messages somewhere.</li>
<li>You bot could be conversational, and might contribute to threads. Check out some NLP to get started on having intelligible conversation! Word2Vec + TensorFlow or Keras might be a place to start. DialogFlow is also great.</li>
<li>Link it up with some other APIs. Maybe you want to be able to interact with a Google Sheet and run some calculations. You might want to send other users a message based on some actions. Integrate <a target="_blank" href="https://api.slack.com/docs/message-buttons">buttons</a>. Perhaps you want to trigger messages based on something else.</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How I built an HR Slack Bot with Node and Botkit ]]>
                </title>
                <description>
                    <![CDATA[ By Alexandre Robin Why create a Slack Bot ? I am an HR professional. More specifically I am a Human Resources Information System (HRIS) Consultant. I work with Application Tracking Systems, Learning Management Systems, and Core HR. But I have never h... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-i-built-an-hr-slack-bot-with-node-and-botkit-6b23b81531bb/</link>
                <guid isPermaLink="false">66d45d5933b83c4378a517ac</guid>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Human Resources ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 17 Sep 2018 16:18:30 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*3qx-9q-OBdf0zcge4Ca8yw.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Alexandre Robin</p>
<h3 id="heading-why-create-a-slack-bot">Why create a Slack Bot ?</h3>
<p>I am an HR professional. More specifically I am a Human Resources Information System (HRIS) Consultant. I work with Application Tracking Systems, Learning Management Systems, and Core HR. But I have never had the opportunity to work with an HR Bot. Which may be the Future of HR.</p>
<p>I read a lot about bots on Slack and Messenger, and used some of them in my daily life — Product Hunt, GitHub and Trello. But for HR purposes, I have never had the opportunity to work with a tool tailored for my needs.</p>
<p>That’s why I decided to work on my own bot.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*cyhNkd0jd7Zd3zBDLaKpSg.png" alt="Image" width="455" height="290" loading="lazy">
<em>Me starting to work</em></p>
<h3 id="heading-my-goals">My Goals</h3>
<p>My bot should be able to manage all the needs a small company could have on Slack:</p>
<ul>
<li>Onboarding</li>
<li>Putting people in touch</li>
<li>Reminders</li>
<li>Announcements</li>
<li>Birthdays /Anniversary</li>
<li>And many more</li>
</ul>
<h3 id="heading-reviewing-the-basics">Reviewing the basics</h3>
<p>For this program, I’ll use:</p>
<ul>
<li>Botkit</li>
<li>Node JS</li>
<li>Express Server</li>
<li>MongoDB</li>
<li>Slack API &amp; of course</li>
</ul>
<p>Botkit is:</p>
<blockquote>
<p>One easy way to build bot users, especially if you already work with <a target="_blank" href="https://nodejs.org/">Node.js</a>, is Howdy’s <a target="_blank" href="https://howdy.ai/botkit/"><strong>Botkit</strong></a>. Botkit is a framework that takes care of most these API gymnastics, so you can focus on your bot’s behavior.</p>
</blockquote>
<p>Exactly what I was looking for :-)</p>
<p>Botkit provides a boilerplate for Slack. But I have chosen to start from scratch to have a better understanding of my bot. However, it’s a good idea to train yourself with a bot created on <a target="_blank" href="https://glitch.com/botkit">Glitch</a>.</p>
<h4 id="heading-how-do-slack-bots-work">How do Slack bots work?</h4>
<p>I am not an expert. I have read again and again Slack and Botkit’s official documentation. I’m still not sure I understood everything. Here is my understanding of a Slack bot’s behavior:</p>
<p>Every App on Slack has a “scope” which is a perimeter on which an app can read or perform actions. A bot is part of an application created and installed on Slack.</p>
<p>Therefore, when you install an app on Slack, you give access to some information and permissions to it. For your bot, you want it to be, at least, able to send and reply to messages of other users.</p>
<p>There are then two cases:</p>
<ol>
<li>You want your bot to react to events happening <strong>directly in Slack</strong></li>
<li>You want your bot to react to events happening <strong>on your server</strong></li>
</ol>
<p>We will view both of them in this post!</p>
<h3 id="heading-getting-started">Getting Started</h3>
<p>Before anything else, you will need a server. In my case, Express.</p>
<p>Below you’ll find my server.js file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">var</span> app = express();
<span class="hljs-keyword">var</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>).Server(app);
<span class="hljs-keyword">var</span> dotenv = <span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>);

<span class="hljs-comment">// configuration ===========================================</span>
<span class="hljs-comment">//load environment variables,</span>
dotenv.load();

<span class="hljs-comment">// public folder for images, css,...</span>
app.use(express.static(__dirname + <span class="hljs-string">'/public'</span>))

<span class="hljs-comment">//parsing</span>
app.use(bodyParser.json()); <span class="hljs-comment">// for parsing application/json</span>
app.use(bodyParser.urlencoded({
    <span class="hljs-attr">extended</span>: <span class="hljs-literal">true</span>
})); <span class="hljs-comment">//for parsing url encoded</span>

<span class="hljs-comment">// view engine ejs</span>
app.set(<span class="hljs-string">'view engine'</span>, <span class="hljs-string">'ejs'</span>);

<span class="hljs-comment">// routes</span>
<span class="hljs-built_in">require</span>(<span class="hljs-string">'./routes/routes'</span>)(app);

<span class="hljs-comment">//botkit</span>
<span class="hljs-built_in">require</span>(<span class="hljs-string">'./controllers/botkit'</span>)


<span class="hljs-comment">//START ===================================================</span>
http.listen(app.get(<span class="hljs-string">'port'</span>), <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'listening on port '</span> + app.get(<span class="hljs-string">'port'</span>));
});
</code></pre>
<p>This port must be public and accessible, not just on a localhost.</p>
<p>For the moment, this server is a blank page, showing and processing nothing.</p>
<p>You’ll then need a Slack App: just follow this <a target="_blank" href="https://api.slack.com/apps">link</a> to create one.</p>
<p>Then, you’ll have to configure your controller. The controller is the brain of your bot. It contains every skill and configuration. Below is my botkit.js file. It has almost the same content found in Botkit’s Starter kit available here: <a target="_blank" href="https://github.com/howdyai/botkit-starter-slack">https://github.com/howdyai/botkit-starter-slack</a></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> mongoUri = <span class="hljs-string">'mongodb://localhost:27017/nameofyourDB'</span>
<span class="hljs-keyword">var</span> database = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../config/database'</span>)({
    <span class="hljs-attr">mongoUri</span>: mongoUri
})
<span class="hljs-keyword">var</span> request = <span class="hljs-built_in">require</span>(<span class="hljs-string">'request'</span>)

<span class="hljs-keyword">if</span> (!process.env.SLACK_ID || !process.env.SLACK_SECRET || !process.env.PORT) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Error: Specify SLACK_ID SLACK_SECRET and PORT in environment'</span>);
    process.exit(<span class="hljs-number">1</span>);
}

<span class="hljs-keyword">var</span> controller = Botkit.slackbot({
    <span class="hljs-attr">storage</span>: database,
    <span class="hljs-attr">clientVerificationToken</span>: process.env.SLACK_TOKEN
})

<span class="hljs-built_in">exports</span>.controller = controller

<span class="hljs-comment">//CONNECTION FUNCTIONS=====================================================</span>

<span class="hljs-built_in">exports</span>.connect = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">team_config</span>) </span>{
        <span class="hljs-keyword">var</span> bot = controller.spawn(team_config);
        controller.trigger(<span class="hljs-string">'create_bot'</span>, [bot, team_config]);
    }
    <span class="hljs-comment">// just a simple way to make sure we don't</span>
    <span class="hljs-comment">// connect to the RTM twice for the same team</span>
<span class="hljs-keyword">var</span> _bots = {};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">trackBot</span>(<span class="hljs-params">bot</span>) </span>{
    _bots[bot.config.token] = bot;
}

controller.on(<span class="hljs-string">'create_bot'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">bot, team</span>) </span>{
    <span class="hljs-keyword">if</span> (_bots[bot.config.token]) {
        <span class="hljs-comment">// already online! do nothing.</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"already online! do nothing."</span>)
    } <span class="hljs-keyword">else</span> {
        bot.startRTM(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err</span>) </span>{
            <span class="hljs-keyword">if</span> (!err) {
                trackBot(bot);
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"RTM ok"</span>)
                controller.saveTeam(team, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, id</span>) </span>{
                    <span class="hljs-keyword">if</span> (err) {
                        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Error saving team"</span>)
                    } <span class="hljs-keyword">else</span> {
                        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Team "</span> + team.name + <span class="hljs-string">" saved"</span>)
                    }
                })
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"RTM failed"</span>)
            }
            bot.startPrivateConversation({
                <span class="hljs-attr">user</span>: team.createdBy
            }, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, convo</span>) </span>{
                <span class="hljs-keyword">if</span> (err) {
                    <span class="hljs-built_in">console</span>.log(err);
                } <span class="hljs-keyword">else</span> {
                    convo.say(<span class="hljs-string">'I am a bot that has just joined your team'</span>);
                    convo.say(<span class="hljs-string">'You must now /invite me to a channel so that I can be of use!'</span>);
                }
            });
        });
    }
});

<span class="hljs-comment">//REACTIONS TO EVENTS==========================================================</span>
<span class="hljs-comment">// Handle events related to the websocket connection to Slack</span>

controller.on(<span class="hljs-string">'rtm_open'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">bot</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'** The RTM api just connected!'</span>)
});

controller.on(<span class="hljs-string">'rtm_close'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">bot</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'** The RTM api just closed'</span>);
    <span class="hljs-comment">// you may want to attempt to re-open</span>
});
</code></pre>
<h4 id="heading-unlocking-the-first-case-react-to-the-events-happening-on-slack">Unlocking the first case: react to the events happening on Slack</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*51uJInZFmmF_0gD3ICFr9Q.jpeg" alt="Image" width="510" height="339" loading="lazy"></p>
<p>When you give the right permissions to your app, every time a message is sent on a channel, Slacks sends a request to your server with some information — the channel ID, the user, the timestamp and most importantly, the content of the message.</p>
<p>If we want our bot to react to a simple message like “Hi”, we have to give Slack an address to send the information to.</p>
<p>In a routes.js file write:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> Request = <span class="hljs-built_in">require</span>(<span class="hljs-string">'request'</span>)
<span class="hljs-keyword">var</span> slack = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../controllers/botkit'</span>)
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">app</span>) </span>{
 app.post(<span class="hljs-string">'/slack/receive'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">req,res</span>)</span>{
<span class="hljs-comment">//respond to Slack that the webhook has been received.</span>
    res.status(<span class="hljs-number">200</span>);
<span class="hljs-comment">// Now, pass the webhook into be processed</span>
    slack.controller.handleWebhookPayload(req, res)
  })
}
</code></pre>
<p>We now have a webhook : <a target="_blank" href="http://your-ip-or-domain:port/slack/receive">http://your-ip-or-domain:port/slack/receive</a></p>
<p>Once Slack is informed of this route via the Event Subscriptions page of your Slack App, it will be able to send it JSON. You will be able to receive it thanks to the parsing part of the server.js file above.</p>
<p>Here is a (simple) schema to explain the process behind it:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*c-Km3PgTIihfbthJC0BFfw.png" alt="Image" width="800" height="312" loading="lazy"></p>
<p>1- SLACK « Here is a JSON file with the latest event on your Slack Channel »</p>
<p>2- SERVER « Okay well received, I send it to Botkit»</p>
<p>3- BOTKIT «Here is a temporary answer, wait a second»</p>
<p>4- BOTKIT « Yeah! I hear a keyword, here is a JSON object with the action to perform »</p>
<p>If we want our bot to react every time it hears “Hello”, we can simply add this .hears() function to our controller:</p>
<pre><code class="lang-javascript">controller.hears([<span class="hljs-string">'hello'</span>, <span class="hljs-string">'hi'</span>], <span class="hljs-string">'direct_message,direct_mention,mention'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">bot, message</span>) </span>{
controller.storage.users.get(message.user, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, user</span>) </span>{
        <span class="hljs-keyword">if</span> (user &amp;&amp; user.name) {
            bot.reply(message, <span class="hljs-string">'Hello '</span> + user.name + <span class="hljs-string">'!!'</span>);
        } <span class="hljs-keyword">else</span> {
            bot.reply(message, <span class="hljs-string">'Hello.'</span>);
        }
    });
});
</code></pre>
<p>Notice the <code>storage.users.get()</code> part in this snippet. Botkit is compatible with almost all the database systems available on the market. I have decided to use MongoDB because it was on my learning list for a long time. Plus the documentation with Botkit is detailed.</p>
<p>Now, we have to let our imagination do the work and find some fun features to create.</p>
<h4 id="heading-second-case-initiate-a-conversation-with-your-bot">Second Case: initiate a conversation with your bot</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*pEUkhsJtGgYrzGvq0xf57Q.jpeg" alt="Image" width="800" height="450" loading="lazy"></p>
<p>For this feature, I wanted my bot to react to events which were not initiated on Slack. For example, do a daily routine. If it’s someone’s anniversary in the company, send them a survey asking their feelings about their first months/weeks.</p>
<p>I have decided to use node-cron: <a target="_blank" href="https://github.com/kelektiv/node-cron">https://github.com/kelektiv/node-cron</a> to manage the daily check.</p>
<p>Here is below a cronjob firing every weekday at 9:00 am. Thanks to the Date() method, the bot gets today’s date and can compare it to the “joinedDate” of the user.</p>
<p>To get only the right users and avoid a forEach loop, we can use a query on our Database:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> dailyCheck = <span class="hljs-keyword">new</span> CronJob(<span class="hljs-string">'00 00 9 * * 1-5'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-comment">/*
         * Runs every weekday (Monday through Friday)
         * at 09:00:00 AM. It does not run on Saturday
         * or Sunday.
         */</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`DailyCheck triggered <span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()}</span>`</span>)

        <span class="hljs-comment">//Gets today's date</span>
        <span class="hljs-keyword">let</span> d = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()
        d.setUTCHours(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>)

        <span class="hljs-keyword">let</span> threeMonthsAgo = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()
        threeMonthsAgo.setUTCMonth(d.getUTCMonth() - <span class="hljs-number">3</span>)
        threeMonthsAgo.setUTCHours(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>)


        <span class="hljs-keyword">let</span> sevenDaysAgo = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()
        sevenDaysAgo.setUTCDate(d.getUTCDate() - <span class="hljs-number">7</span>)
        sevenDaysAgo.setUTCHours(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>)


        controller.storage.users.find({
            <span class="hljs-string">"joinedDate"</span>: {
                <span class="hljs-string">"$eq"</span>: +sevenDaysAgo
            }
        }, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, user</span>) </span>{
            user.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">member</span>) </span>{
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Message was sent to <span class="hljs-subst">${member.name}</span>(<span class="hljs-subst">${member.id}</span>)`</span>)
                bot.startPrivateConversation({
                    <span class="hljs-attr">user</span>: member.id
                }, Conversations.sendSurvey7)
            })
        })
    }, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-comment">/* This function is executed when the job stops */</span>
    }, <span class="hljs-literal">true</span>,
    <span class="hljs-comment">/* Start the job right now */</span>
    timeZone = <span class="hljs-string">'Europe/Paris'</span> <span class="hljs-comment">/* Time zone of this job. */</span> )
</code></pre>
<p>And… Tada!</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*MSZ0zXebVZ_wwCKP." alt="Image" width="800" height="533" loading="lazy">
_“A robot named Pepper holding an iPad” by [Unsplash](https://unsplash.com/@agkdesign?utm_source=medium&amp;utm_medium=referral" rel="noopener" target="_blank" title=""&gt;Alex Knight on &lt;a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral" rel="noopener" target="<em>blank" title=")</em></p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>After more than a year of being a camper and learning to code, I am really happy to be able to start and finish a project like this one. I now have a bot working and performing almost all the actions I had in mind at the design phase. And I still have a lot of ideas!</p>
<p>I am still working on this bot. The GitHub repository is available here: <a target="_blank" href="https://github.com/alexandrobin/hrbot">https://github.com/alexandrobin/hrbot</a>. Some of the commits are in French, but the codebase is commented in English. :-)</p>
<p>Besides, it’s quite easy to deploy it on Heroku with a Mongolab database if you don’t have a server!</p>
<p>If you have some suggestions or are interested by this article and project, feel free to leave a comment ! I would be happy to discuss with you.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to build a Meetupbot for Slack using Node.js ]]>
                </title>
                <description>
                    <![CDATA[ By premprakashsingh What is Slack? If you are new to Slack, it’s a great platform for team collaboration and instant messaging used in and out of organizations to help team communication and collaboration. I first used Slack for a study group. You c... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-meetupbot-for-slack-using-node-js-618725aa4c6e/</link>
                <guid isPermaLink="false">66c34f8b0fa3812cdd5eaa2b</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Meetup ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sat, 13 Jan 2018 19:49:56 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*YQTE7lkH8LNnkguLdzaDkA.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By premprakashsingh</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*qj03MmP47z5lduohrVC5aA.png" alt="Image" width="512" height="512" loading="lazy"></p>
<h3 id="heading-what-is-slack">What is Slack?</h3>
<p>If you are new to <a target="_blank" href="https://slack.com/">Slack</a>, it’s a great platform for team collaboration and instant messaging used in and out of organizations to help team communication and collaboration.</p>
<p>I first used Slack for a study group. You can create different channels to separate messages and discussions. You can create private channels as well to keep messages private in a team.</p>
<p>The best functionality is that it also allows integrations on it’s platform. And this is what makes it different than other messaging and collaboration platforms.</p>
<p>You can integrate Google Calendar, Twitter, Trello, and more. It also let’s you create custom applications like bots.</p>
<h3 id="heading-project">Project</h3>
<p>In this post, I will walk you through building a <a target="_blank" href="https://meetupbotteam.github.io/meetupbot-landing-page/">MeetupBot</a> for Slack using Node.js. It will give you list of meetups going on near your location based on your interest.</p>
<p>Feeling excited?</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*l2W7LDBTPVvEAArgB4Sg2w.gif" alt="Image" width="480" height="360" loading="lazy"></p>
<p>It will use Slack’s slash commands. You can type <strong>/meetupbot</strong> from within Slack to call the <a target="_blank" href="https://meetupbotteam.github.io/meetupbot-landing-page/">MeetupBot</a> and it will greet you along with the list of commands.</p>
<p>I built this project as part of <a target="_blank" href="https://medium.com/chingu">Chingu</a> cohort with my 2 team members <a target="_blank" href="https://github.com/zamhaq">Zameer</a> and <a target="_blank" href="https://github.com/nusli">Linus</a></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*VTIZoHI-bb-CuXb85G6DHg.png" alt="Image" width="655" height="313" loading="lazy"></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*2ROpGK4lel2ZHV5rG8PI0A.png" alt="Image" width="645" height="499" loading="lazy"></p>
<p>You will need basic knowledge of Node.js and how APIs work. Let’s get started.</p>
<h3 id="heading-steps-for-building-meetupbot-for-slack">Steps for building MeetupBot for Slack</h3>
<h4 id="heading-step-1-project-setup">Step 1 — Project Setup</h4>
<p>my repo URL : <a target="_blank" href="https://github.com/PREMPRAKASHSINGH/slack-meetup-bot">slack-meetup-bot</a><br>Glitch : <a target="_blank" href="https://glitch.com/">glitch.com</a><br>Meetup_api : <a target="_blank" href="https://www.meetup.com/meetup_api/">meetup.com/meetup_api</a></p>
<ul>
<li>First fork my repository <a target="_blank" href="https://github.com/PREMPRAKASHSINGH/slack-meetup-bot">here</a>.</li>
<li>Then go to <a target="_blank" href="https://glitch.com/">glitch.com</a> and create a project and edit the project name to a shorter name.</li>
<li>Click on the <strong>Project Name</strong> &amp;g<strong>t; Advanced Opti</strong>ons. Then cli<strong>ck Import from Git</strong>Hub. You first need to grant access of the GitHub repo to import your repositories into Glitch.</li>
<li>Go to <a target="_blank" href="https://www.meetup.com/meetup_api/">Meetup Api here</a> and click on <strong>API Key</strong> tab and save that as you will pass it with every request to Meetup API.</li>
<li>In your Glitch project open <code>**.env**</code> file and set a variable <strong>SECRET</strong> as Your Meetup API Key as <code>SECRET=&lt;MeetupApiK</code>ey&gt;</li>
<li>Click on <strong>Show Live</strong> in Glitch and you will get your Glitch project URL.</li>
</ul>
<h4 id="heading-step-2-create-a-slack-app">Step 2 — Create a Slack App</h4>
<ul>
<li>Go to <a target="_blank" href="https://api.slack.com/">Slack apps</a> and then click on <strong>Your Apps</strong> &amp;g<strong>t; Create New</strong> App.<br>It will show you following screen:</li>
</ul>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*EOvPe85KeYY8q_refsTtyQ.png" alt="Image" width="566" height="566" loading="lazy"></p>
<p>Enter the App Name and select <strong>Development Slack Workspace</strong> and click on <strong>Create App</strong>. Now we need to do 3 things to see it working in our Slack workspace.</p>
<p>On the next screen you will see your App Configuration page with following things:</p>
<ol>
<li>Activate incoming webhooks.</li>
<li>Create Slash commands.</li>
<li>Install your app to your workspace.</li>
</ol>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*EusJC6rdy4lN82UtELAbMw.png" alt="Image" width="727" height="535" loading="lazy"></p>
<ul>
<li>Now click on <strong>Incoming Webhooks</strong> and activate it.<br>Incoming Webhooks allows you to post messages into Slack.</li>
<li>Next thing click on <strong>Slash Command</strong>” and create one as <strong>/meetupbot</strong>. Command as <code>/meetupbot</code>, <strong>Request URL</strong> as <code>**&lt;glitch-project-url&gt;/mee**</code>tupbot, and <strong>add a Short Descr</strong>iption <strong>and a Usag</strong>e Hint.</li>
</ul>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*eWKdYfwrTuq-TrbrVBn6ng.png" alt="Image" width="623" height="520" loading="lazy"></p>
<ul>
<li>By activating Incoming Webhooks and creating Slash Commands you should have already got a green tick on Permissions.</li>
<li>Now click on <strong>Install your app to your workspace</strong> and that will take you to next screen to confirm and authorize before installing. And now you are good to go.</li>
</ul>
<h4 id="heading-step-3-test-it-in-your-channel">Step 3 — Test it in your channel</h4>
<p>Open your Slack team channel and type <strong>/meetupbot</strong> and you should be able to see your commands popping up. Click <strong>Enter</strong> and you will see a greeting message from MeetupBot and a list of commands that you can use.</p>
<p>Since you have created only one slash command go to your App page and create 1 more commands as <strong>/meetupbot-show</strong> with <strong>Request URL</strong> as <code>&lt;glitch-project-url&gt;/meetupbo</code>t-show (Follow step 2 — create Slack Command).</p>
<p>Now try this command, type <code>**/meetupbot-show San Francisco and JavaScript**</code> then hit Enter and you will see list of JavaScript meetups in San Francisco with details like Name of Event and Meetup Group, Date of Meetup, Status, Venue and Rsvp Count. Click on Event and it will take you to their Meetup Event page.</p>
<p>So that’s it, Congratulations you have successfully created a MeetupBot for Slack using Node.js.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*6MYbG_q1sUHux_vYTIBW-w.gif" alt="Image" width="400" height="400" loading="lazy"></p>
<h3 id="heading-lets-understand-the-code">Lets understand the code.</h3>
<p>We are using the Google Geocode API to get Latitude and Longitude from location/address parameter that is passed in the command. This latitude and longitude along with interest parameter is then passed to the Meetup API to get a list of meetups.</p>
<p>Also we are using Express.js and JavaScript Promises, npm packages Moment.js for parsing dates and Request to make API calls.</p>
<p>What happens when you call <code>**/meetupbot**</code> ? It makes a Post request to <code>glitch-project-url/meetupbot</code>.</p>
<p>The request body contains <code>user_name</code>, <code>text</code> and other info. The reply object is the JSON response format for the Slack API.</p>
<p>What happens when you call <code>**/meetupbot-show**</code> ? It makes a Post request to <code>glitch-project-url/meetupbot</code>. The request body contains the <code>user_name</code>, <code>text</code> (such as location and interest separated by “&amp;”) and other info.</p>
<p>We first make sure the location and interest parameters sent with the command are not blank.</p>
<p>Then we pass location to <code>getGeocode</code> method which is a JavaScript Promise that make calls to Google Geocode API and returns Latitude and Longitude, which is then passed to <code>getMeetupEvents</code> Promise to get list of meetup by making a call to Meetup API.</p>
<p>The Meetup API returns an array of meetup event objects and we iterate through this array to make an array of event objects in Slack response format and keep pushing it in the <code>attachment</code> array which we created in the start.</p>
<p>And that reply with event attachments is then returned as response and is shown in your Slack.</p>
<p>This response will only be visible to you (the user who calls the bot ) and won’t disturb other members of channel.</p>
<p>In the above code we have 2 Promises as follows:</p>
<ul>
<li><code>getGeoCode()</code> — This take location as parameter and makes an API call to Google Geocode API with location as query string and returns <code>latlong</code>.</li>
<li><code>getMeetupEvents()</code> — This takes location and interest as parameters and makes API call to Meetup API containing the API Key, Latitude, Longitude, text or interest and radius as query string parameters.</li>
</ul>
<p>The above code uses JavaScript Promise which is basically used to handle asynchronous operations. It allows you to write asynchronous code that is similar in style to synchronous code.</p>
<p>Also helps in avoiding nested callbacks by using chainable <code>then</code>. If you have nested callbacks in code then it looks like pyramid structure also known as “callback hell”.</p>
<h3 id="heading-official-meetupbot">Official MeetupBot</h3>
<p>The official MeetupBot has one more command as <strong>/meetupbot-find</strong> to get list of meetup group in your location/area and also has Oauth code so that you can install it by clicking add to slack button.</p>
<p>You can find it here <a target="_blank" href="https://meetupbotteam.github.io/meetupbot-landing-page/">MeetupBot landing page</a> and <a target="_blank" href="https://github.com/MeetupBotTeam/slack-meetup-bot">MeetupBot github repo</a>. Start using it now.</p>
<p>Did you find this article useful? Write your comments below.</p>
<p>If you found this article helpful, do share with your friends and give this couple of claps.</p>
<p>— Thank you :)</p>
<p>Originally posted <a target="_blank" href="http://howtocoder.com/blog/how-to-build-meetupbot-for-slack-using-nodejs">here</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Tutorial: Building a community on-boarding app with Serverless, StepFunctions, and StackStorm… ]]>
                </title>
                <description>
                    <![CDATA[ By Dmitri Zimine Build a real-world serverless application on AWS with Serverless framework and ready-to-use functions from StackStorm Exchange open-source catalog. Episode One | Episode Two | Episode Three | Episode Four Read on if you are: A serve... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/tutorial-building-a-community-on-boarding-app-with-serverless-stepfunctions-and-stackstorm-b2f7cf2cc419/</link>
                <guid isPermaLink="false">66c363b40cede4e9b1329d18</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 18 Dec 2017 18:15:36 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*QdErjJE8X0jg3SfR7QyBVQ.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Dmitri Zimine</p>
<p>Build a real-world serverless application on AWS with <a target="_blank" href="https://serverless.com/framework/">Serverless framework</a> and ready-to-use functions from <a target="_blank" href="https://exchange.stackstorm.org">StackStorm Exchange</a> open-source catalog.</p>
<p>Episode One | <a target="_blank" href="https://medium.com/@dzimine/building-community-sign-up-app-with-serverless-stepfunctions-and-stackstorm-exchange-episode-2-b1efeb1b9bd6">Episode Two</a> | <a target="_blank" href="https://medium.com/@dzimine/building-a-community-sign-up-app-with-serverless-stepfunctions-and-stackstorm-exchange-episode-6efb9c102b0a">Episode Three</a> | <a target="_blank" href="https://medium.com/@dzimine/building-a-community-sign-up-app-with-serverless-stepfunctions-and-stackstorm-exchange-episode-7c5f0e93dd6">Episode Four</a></p>
<p>Read on if you are:</p>
<ul>
<li>A serverless developer using <a target="_blank" href="https://serverless.com/framework/">Serverless framework</a> who wants to check out ready-to use functions from the StackStorm Exchange open-source catalog,</li>
<li>A <a target="_blank" href="https://stackstorm.com">StackStorm</a> user who lives in AWS and misses the breadth of <a target="_blank" href="https://exchange.stackstorm.org">StackStorm Exchange</a> integrations there.</li>
<li>Anyone who has 2 hours to follow-along and learn serverless with something more elaborate and real-life than a <a target="_blank" href="https://serverless.com/framework/docs/providers/aws/examples/hello-world/">Hello-world example</a>.</li>
</ul>
<p>If you only got 8 minutes to spare, skim the text and examples, spend an extra 30 seconds browsing <a target="_blank" href="https://exchange.stackstorm.org">StackStorm Exchange</a> to see the potential, and bookmark this post to get back to it when you need it.</p>
<h4 id="heading-intro">Intro</h4>
<p>When I <a target="_blank" href="https://medium.com/@dzimine/exploring-serverless-with-python-stepfunctions-and-web-front-end-8e0bf7203d4b">explored Serverless with Python, StepFunctions, and Web Front-end</a>, one thing I missed is a catalog of reusable integrations. Something like <a target="_blank" href="https://docs.microsoft.com/en-us/azure/connectors/apis-list#popular-connectors">200 connectors for Azure Logic apps</a>. Or <a target="_blank" href="https://exchange.stackstorm.org">130 integration packs for StackStorm</a>.</p>
<p>When we need to wire in Slack, Jira, Stripe, or Nest, could we skip digging into their APIs and authentication intrinsics, and just grab a ready-to-use function?</p>
<p>Now we can do exactly that: StackStorm just <a target="_blank" href="https://stackstorm.com/2017/12/14/stackstorm-exchange-goes-serverless">announced a plugin for Serverless framework</a> that turns integrations from StackStorm Exchange into AWS Lambda functions.</p>
<p>In this tutorial, I’ll show how to use the plugin and Exchange integrations, in the context of building a serverless community on-boarding application from a ground-up. Let’s make this conversational and fun.</p>
<p>I assume no familiarity with either <a target="_blank" href="https://serverless.com/framework/">Serverless framework</a> nor StackStorm. But you should know how to code, and be smart to compensate for mistakes and omissions I’ll inevitably make.</p>
<p>We will be going slow, with excruciating details, thus it is going to be four episodes.</p>
<p>In this first episode, I’ll set everything up and deploy my first StackStorm Exchange action.</p>
<p>In the next episode, we’ll add more actions.</p>
<p>In the third episode I’ll wire them together with a AWS StepFunction.</p>
<p>In the fourth episode we’ll add Web Front-end with the Reflection and Summary. Each episode will take about an hour to follow.</p>
<p>Ready? Let’s rock.</p>
<h3 id="heading-the-application">The application</h3>
<p>We will build a community on-boarding application. Actually, rebuilding from scratch the one we run at StackStorm. It’s like <a target="_blank" href="http://rauchg.com/slackin/">SlackIn</a> with a multi-step customizable on-boarding workflow. The app presents a registration web-form, which passes new user info through API Gateway to the StepFunction workflow that carries on-boarding steps.</p>
<p>In my case, the steps are 1) invite users to Slack 2) create contact record in ActiveCampaign CRM tool and 3) put a user record into internal DynamoDB table. Here is how it looks:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*QdErjJE8X0jg3SfR7QyBVQ.png" alt="Image" width="800" height="492" loading="lazy"></p>
<p>You can find the previous implementation <a target="_blank" href="https://github.com/dzimine/slack-signup-serverless">on github</a>, or <a target="_blank" href="https://stackstorm.com/community-signup">use it and join StackStorm community</a> on Slack for questions about StackStorm Exchange integration.</p>
<h4 id="heading-getting-ready">Getting Ready</h4>
<p>First, you’ll need AWS account, NodeJS, Docker, and Serverless framework. And Slack! As our first action will be inviting users to Slack.</p>
<ol>
<li>Make sure <a target="_blank" href="https://nodejs.org/">Node.JS</a> is installed, and it’s version is <code>8.4.0</code> or higher.</li>
<li><a target="_blank" href="https://serverless.com/framework/docs/providers/aws/guide/installation/">Install Serverless framework</a>, and setup AWS credentials for it <a target="_blank" href="https://serverless.com/framework/docs/providers/aws/guide/credentials/">following this guide</a>.</li>
<li><a target="_blank" href="https://docs.docker.com/engine/installation/">Install Docker</a>. The plugin uses it for build environment to make the lambdas binary compatible to AWS execution environment no matter what OS you’re using for development. There is a way to make it work without Docker but don’t take chances.</li>
<li>Slack! Our action will require admin access and will be using undocumented Slack API (docs here, pun intended) to invite new users. The easiest is to just go ahead <a target="_blank" href="https://slack.com/get-started">create a new team</a>. Takes 4 min. Slack won’t mind — they’ll show growth to their VC.</li>
<li>Once the workspace is created, time to get an authentication token. Go to <a target="_blank" href="https://api.slack.com/custom-integrations/legacy-tokens">api.slack.com/custom-integrations/legacy-tokens</a>. Fear not the “Legacy Warnings”: this tutorial will turn legacy before they do. Do what they say, get your token.</li>
</ol>
<p><strong>PRO TIP:</strong> Use <a target="_blank" href="https://github.com/StackStorm-Exchange/stackstorm-slack#obtaining-auth-token">this quick hack</a> to get and use your own user’s auth token. Much faster, good for playing and debugging. But please never, never use it for production!</p>
<h3 id="heading-create-a-project-add-a-first-action">Create a project, add a first action</h3>
<p>Try <code>sls --help</code> to make sure that at least something works. <code>sls</code> is a shorthand for <code>serverless</code>, the Serverless framework CLI. Now put your coffee aside, time to create a project. Some folks like using templates that come with serverless: <code>sls create --template</code>. I prefer to start from scratch:</p>
<pre><code>mkdir slack-signup-serverless-stormlesscd slack-signup-serverless-stormless
</code></pre><pre><code>npm init
</code></pre><pre><code># Once you answer questions, the project is set up.
</code></pre><p>Next, install <code>[serverless-plugin-stackstorm](https://github.com/StackStorm/serverless-plugin-stackstorm)</code>, the one that plugs in the <a target="_blank" href="https://exchange.stackstorm.org">StackStorm Exchange</a> actions.</p>
<pre><code>npm install --save-dev serverless-plugin-stackstorm
</code></pre><p>… and create a minimal <code>serverless.ym</code> file so that <code>sls</code> command will pick up the plugin:</p>
<p>Now, <strong>create the first action</strong>. I’ll use a battle-tested <a target="_blank" href="https://exchange.stackstorm.org/#slack">Slack pack from StackStorm Exchange</a>. Which action, you say? Ok, StackStorm Exchange is not smart enough yet to show pack’s action list, but <code>sls stackstorm</code> will rescue us.</p>
<p><code>sls stackstorm info --pack slack</code></p>
<p>Oh my! There’re so many! what are they? I guess I need a PR to print action description. Meantime, <code>| grep admin</code> will get us the one we need: <code>slack.users.admin.invite</code>. Let's query the action for it's parameters:</p>
<pre><code>$ sls stackstorm info --action slack.users.admin.inviteslack.users.admin.invite ...... Send an invitation to join a Slack OrgParameters  attempts [integer]  ......... description is missing  channels [string]  .......... Channels to auto join.  email [string] (required) ... Email address to send invitation to.  first_name [string]  ........ Users first name  set_active [boolean]  ....... Should the user be active.  token [string]  ............. Slack API token.Config  action_token [string]  ...... Slack Action token.  admin [object]  ............. Admin-action specific settings.  post_message_action [object]   Post message action specific settings.  sensor [object]  ............ Sensor specific settings.
</code></pre><p>Awesome! We can see that there is only one required parameter, <code>email</code>, but I'll add <code>first_name</code> to stay conversational. The token can be passed as parameters, or as config. And if I choose to use config, my prior tribal knowledge hints that the <code>admin [object]</code> requires only <code>admin_token</code>. The very one I asked you to remember when you were setting up Slack workspace.</p>
<p><strong>PRO TIP:</strong> While we are still polishing the plugin to expose all the Config details, you can find it out by exploring StackStorm Exchange pack config schema in <code>config.schema.yaml</code> file. For example, here is <a target="_blank" href="https://github.com/StackStorm-Exchange/stackstorm-slack/blob/master/config.schema.yaml#L47-L78">config.example.yaml</a> for our Slack pack.</p>
<p>Now we have all we need to create the heart of any Serverless project: the <code>serverless.yml</code>. Here it comes:</p>
<p>This is a good time to learn a bit of Serverless. Take a quick break to skim <a target="_blank" href="https://serverless.com/framework/docs/providers/aws/guide/intro/">Core Concepts</a> and bookmark <a target="_blank" href="https://serverless.com/framework/docs/providers/aws/guide/serverless.yml/">Serverless.yml Reference</a>.</p>
<p>I threw in the <code>events</code> section in lines 9:12 so that we can invoke the function with REST call through AWS API Gateway endpoint. Serverless framework will do all the Gateway magic.</p>
<p>Note that this default configuration instructs API Gateway to pass the REST POST call with POST body under the <code>body</code> key (<a target="_blank" href="https://serverless.com/framework/docs/providers/aws/events/apigateway#simple-http-endpoint">details here</a>. When we POST <code>{"first_name": "Santa", "email": "santa@mad.russian.xmas.com"}</code>, the event passed to the Lambda is:</p>
<pre><code>...<span class="hljs-string">"body"</span>: {    <span class="hljs-string">"first_name"</span>: <span class="hljs-string">"Santa"</span>,     <span class="hljs-string">"email"</span>: <span class="hljs-string">"santa@mad.russian.xmas.com"</span>}
</code></pre><p>Knowing the input data structure is important to map it to the action input parameters. It’s intuitive: <code>input</code> represents <code>event</code> parameter of <a target="_blank" href="http://docs.aws.amazon.com/lambda/latest/dg/python-programming-model-handler-types.html">AWS Lambda programming model</a>(BTW should we call it <code>event</code>? Vote with a PR!).</p>
<p><a target="_blank" href="http://jinja.pocoo.org/">Jinja</a> is used to map the inputs; our JavaScript friends who're less familiar with this common Python tool find it intuitive enough in simple cases; and Stack-overflow is full of magic Jinja tricks.</p>
<p>In lines 16:17 of <code>serverless.yml</code> I map the two parameters from input body to desired action input parameters. Optionally, you can also form a function output from action results. I’ll keep it simple for now (line 20:22) and save more tricks for later.</p>
<p>To keep the config separate, I created a file <code>env.yml</code> that and put my config parameters in it:</p>
<pre><code># ./env.yml# WARNING: Don<span class="hljs-string">'t commit to Github :)slack:  admin_token: "xoxs-111111111111-..."  organization: "my-team"</span>
</code></pre><p>Then I used it in <code>serverless.yml</code> like this: <code>admin: ${file(env.yml):slack}</code>. Note how this syntax puts the object from the key in the file to the key in <code>serverless.yml</code>.</p>
<p>Ok, that’s it! The function is ready to fly to AWS with <code>sls deploy</code>. But I take it <strong>sloooow</strong>. Step by step. First, I’ll package it locally.</p>
<pre><code>sls package
</code></pre><p>The very first time takes a long time as this is the time when the plugin installs its runtime dependencies. It pulls the Docker images from the Hub. It installs StackStorm runners — the code that knows how to run StackStorm Exchange packs. It pulls the <code>slack</code> pack from Exchange. It installs <code>slack</code> pack python dependencies. It does a lot of work for us, and it takes time. Good news: it's only the first time.</p>
<p>Oh, did I mention that you must be connected? Or do we assume internet connection a basic commodity like breathing air and electric power? At least before FCC repeals Network Neutrality? So yes, you need internet connection to live in the serverless world.</p>
<p>Now let’s run this locally.</p>
<pre><code>sls stackstorm docker run --<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">InviteSlack</span> --<span class="hljs-title">verbose</span> \--<span class="hljs-title">data</span> \'</span>{<span class="hljs-string">"body"</span>: {<span class="hljs-string">"first_name"</span>: <span class="hljs-string">"Santa"</span>, <span class="hljs-string">"email"</span>: <span class="hljs-string">"santa@mad.russian.xmas.com"</span>}}<span class="hljs-string">'</span>
</code></pre><p>Local runs happen in a container — you’ll see <code>Spin Docker container to run a function</code> CLI output. It takes a bit longer, but ensures that the execution environment matches AWS lambda very closely, so better safe than sorry.</p>
<p>When I debug input and output parameter transformations, I may not want to call the actual function, like in case of Slack rate-limiting API. Use <code>--passthrough</code> parameter that tells the plugin to do the dry-run and skip the action invocation.</p>
<p>Now we are really ready. Let’s deploy the function to AWS, and run it “serverless”.</p>
<pre><code>sls deploy
</code></pre><p>It will take some while — now it’s serverless (and honestly, our bundle is a bit bloated, patience! plugin developers are currently busy solving other problems, we will optimize it as soon as we can)</p>
<p><strong>PRO TIP:</strong> if something goes wrong at this point, most likely something is not right with your AWS setup. Go back to “Getting Ready, step 2”. Read <a target="_blank" href="https://serverless.com/framework/docs/providers/aws/guide/installation/">Serverless Installation</a> doc. Google, Stack-overflow, Serverless <a target="_blank" href="https://gitter.im/serverless/serverless">Gitter channel</a> or <a target="_blank" href="https://forum.serverless.com/">Forum</a>.</p>
<p>You might be curious to see how it looks in the AWS console. If the PRO in you is saying “no, you should stay cool and use CLI”, don’t listen. Go indulge yourself, open a browser and take a good look at your Lambda. While there, you might also inspect the API Gateway endpoint that <code>sls</code> created for you.</p>
<p>But to test it, we’ll go back to terminal. Here is how to run your Lambda with <code>sls</code>:</p>
<pre><code>sls invoke --<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">InviteSlack</span> --<span class="hljs-title">log</span> --<span class="hljs-title">data</span> '</span>{<span class="hljs-string">"body"</span>: {<span class="hljs-string">"first_name"</span>: <span class="hljs-string">"Santa"</span>, <span class="hljs-string">"email"</span>: <span class="hljs-string">"santa@mad.russian.xmas.com"</span>}}<span class="hljs-string">'</span>
</code></pre><p>Finally, let’s POST to the API endpoint. The endpoint was printed at the end of <code>sls deploy</code> and you should have taken notice, but it's OK if you didn't: you can always get it by typing <code>sls info</code>.</p>
<p>You <code>curl</code> lovers go ahead use it to POST; be sure to set <code>Content-Type=application/json</code> header. Me - I'll show off with <a target="_blank" href="https://httpie.org/">httpie</a>, aka CURL for humans:</p>
<pre><code># DON<span class="hljs-string">'T copy-paste! Use YOUR endpoint!</span>
</code></pre><pre><code>http --json POST  https:<span class="hljs-comment">//YOUR-ENDPOINT.amazonaws.com/dev/invite \email=test@example.com first_name=Dmitri</span>
</code></pre><p>How did it go? Everything worked, at least for me. Let’s fire yet another most useful <code>sls</code>command to check the CloudWatch logs:</p>
<pre><code>sls logs --<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">InviteSlack</span></span>
</code></pre><p>Success! And the end of the first episode. Enough for now: Christmas time is here, take it slow, enjoy!</p>
<p>The code example so far is on Github at <a target="_blank" href="https://github.com/dzimine/slack-signup-serverless-stormless/tree/DZ/1-add-first-action">1-add-first-action</a>.</p>
<p><a target="_blank" href="https://medium.com/@dzimine/building-community-sign-up-app-with-serverless-stepfunctions-and-stackstorm-exchange-episode-2-b1efeb1b9bd6"><strong>Episode 2</strong></a>: <strong>Adding more actions</strong></p>
<p>Hope this helped you learn something new, find something interesting, or provoked some good thoughts. Please share your thoughts in the comments here, or tweet me <a target="_blank" href="https://twitter.com/dzimine">@dzimine</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How a hackathon is kind of like a real-life triathlon — but with more caffeine ]]>
                </title>
                <description>
                    <![CDATA[ By Alec Jones I’m 15, and I recently signed up for the Global Hackathon by Product Hunt. It runs for the month of November and there are 7,950 people participating from around the world ?. Crazy! From what I’ve seen in my 3 years as a student program... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-a-hackathon-is-kind-of-like-real-life-triathalon-but-with-more-caffeine-50226ded1708/</link>
                <guid isPermaLink="false">66c34ca330aba6677fb9f9c9</guid>
                
                    <category>
                        <![CDATA[ Life lessons ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ startup ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 29 Nov 2017 21:26:47 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*tXS2niLfdGKZxdCvgnAjCA.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Alec Jones</p>
<p>I’m 15, and I recently signed up for the <a target="_blank" href="https://www.producthunt.com/hackathon">Global Hackathon</a> by Product Hunt. It runs for the month of November and there are 7,950 people participating from around the world ?. Crazy!</p>
<p>From what I’ve seen in my 3 years as a student programmer, developers love hackathons. They’re an opportunity for product makers meet up, drink Red Bulls, and stay up all night coding.</p>
<p>It’s kind of like a triathlon for developers, who often go for days with minimal sleep. It’s a hard-fought race to the finish (if you even manage to finish). And it’s a chance to compete under pressure.</p>
<h3 id="heading-the-three-stages-of-a-hackathon">The three stages of a hackathon</h3>
<p>Shortly after I registered for the month-long event, I realized that competing in this hackathon is similar to entering a real triathlon. Just like in a regular triathlon, there are 3 stages, which all require a lot of skill and endurance:</p>
<ul>
<li>The first stage is about coming up with the idea (the swim).</li>
<li>The second stage is about turning the idea into something real (the cycle).</li>
<li>The third and final stage telling everyone how helpful your product is and convincing them to try it (the run).</li>
</ul>
<h3 id="heading-compressing-all-of-these-stages-into-a-short-moment-in-time">Compressing all of these stages into a short moment in time</h3>
<p>I’ve already built something that started out as an idea. It’s called Christopher Bot, a Facebook Messenger bot that helps high school students remember their homework, and it’s been written about <a target="_blank" href="http://www.bbc.com/news/technology-39013950">here</a> and <a target="_blank" href="https://www.theglobeandmail.com/news/british-columbia/bc-teen-creates-facebook-chatbot-to-help-students-stay-organized/article34278474/">here</a> and <a target="_blank" href="http://www.metronews.ca/news/canada/2017/03/12/facebook-tool-created-by-b-c-teen-to-plan-homework-gains-popularity-overseas.html">here</a>. It also got acquired! But, you can read about that <a target="_blank" href="https://medium.freecodecamp.org/the-ups-and-downs-of-building-and-marketing-a-chat-bot-when-youre-14-8a072830b43c">in my other Medium story here</a>.</p>
<p>Before choosing to work on Christopher Bot, I had lots of ideas for products to build. My dad and I talked about how some ideas catch on, and some don’t. The ideas that catch on always seem to solve a problem.</p>
<p>If enough people have a problem and your product solves it well, and you can find a way to share your solution with those people, it has a good chance of succeeding.</p>
<p>I followed that advice when I chose to work on a homework bot. Students often forget what homework they’re assigned during the day, so I needed to build something that would stop people from forgetting their assignments. Christopher Bot was born.</p>
<p>I’m using that same advice in the Product Hunt hackathon.</p>
<p>I needed to find a problem. And in that problem, there would be a solution.</p>
<p>But where to begin?</p>
<h3 id="heading-swimming-for-an-idea">Swimming for an idea</h3>
<p>In the Product Hunt competition, there are a bunch of existing platforms you can build for: Google Assistant, Slack, Blockchain, Artificial Intelligence and Augmented Reality. You don’t have to build for any of them, but some cool prizes are awarded within these specific categories.</p>
<p>Seeing these options actually made things easier for me. It narrowed down the list of possible problems to solve. All I had to do was find a problem in one of these categories.</p>
<p>I’m not yet familiar with blockchain technology… or AR or AI. So that made things even easier. Google Assistant is cool and could be interesting to develop for, but it turns out I’ve spent a fair bit of time using Slack.</p>
<p>I think Slack is pretty awesome and I’ve tried out a bunch of Slack apps. So I decided to build something in the Slack category.</p>
<p>At first, I thought about building something that would remind you about all your mentions in public channels — because once you click on a channel with a notification for you, the notification disappears and you have to respond right away or you might forget that you were mentioned.</p>
<p>But then came some bad news. My dad (who likes to talk these things through with me) experiences this notification problem, but he showed me that Slack already has a built-in solution.</p>
<p>You can mark important messages where you’re mentioned as “unread”, and you can even tell Slack bot to remind you about an important message after a specific period of time.</p>
<p>So it was back to the drawing board for me. ☹️</p>
<p>During the same conversation where I found out that bit of disappointing news, I also noticed that <strong>there’s no easy way to DM multiple people at the same time</strong>.</p>
<p>Was this even a real problem? It turns out (based on some quick Google searches) that people have asked about how to do this in Slack — and even in products that compete with Slack.</p>
<p>If you’re part of a big team and you want to ask multiple people the same question, but you don’t want everyone in a channel (or in a group DM) to see their responses, there’s no easy way to do it in Slack.</p>
<p>I checked hundreds of existing apps in the Slack app directory — and couldn’t find anything to solve this particular problem. But I found that the most popular slack apps are the ones that solved other problems for teams… another reminder that I needed to work on solving a real problem.</p>
<p>For example, getting everyone on your team to vote on something (like where to go for lunch) is a pain. So the people behind Simplepoll decided to solve that problem by enabling someone on a Slack team to easily create a quick poll, ask their team to vote, collect the responses, and count up the votes.</p>
<p>They found a problem common to many teams and built a nice simple solution. Which is exactly what I wanted to do.</p>
<p>My hackathon project is an idea that’s based on a real problem for Slack teams…</p>
<p>The problem: You want to ask multiple people the same question but you don’t want the entire team — or even a portion of the team — to see the question OR the responses. You can’t use a group DM or public channel… because everyone will see what you wrote, and see the responses (unless people are instructed to respond privately in a DM). It’s not very ideal.</p>
<p>Another less than ideal solution is to go down the list of team members and create a new DM, one after the other, by pasting the same message for each person. It’s so tedious that I think most people wouldn’t even bother.</p>
<p>So my entry for this hackathon is called <a target="_blank" href="https://www.producthunt.com/upcoming/multidm"><strong>MultiDM</strong></a>, a very simple Slack app that gives you and your team the ability to DM multiple people at the same time — using a single message, sent from any channel.</p>
<p>(Swim stage finished!)</p>
<p>Let’s say you think of an important HR-related question you want to ask 5 people on your team. And let’s say you’re already in the #general channel.</p>
<p>From the channel, you would just type “<em>/multidm @john @jane @henry @samantha @jake I need your birthdate for HR — can you please let me know ASAP?</em>”</p>
<p>Nobody will see the message you typed into #general. Instead, each of the 5 team members will receive a normal-looking DM from you — which they can respond to normally, and you’ll receive their answers without anyone else seeing the answers or even knowing that you asked multiple people the same question.</p>
<p>So that’s it. Next up is the long, difficult cycling stage. ??</p>
<p>If you’re interested, you can sign up to be one of the first to try <a target="_blank" href="https://www.producthunt.com/upcoming/multidm">MultiDM</a>.</p>
<p>Thanks for reading about my process for choosing a product idea. Most ideas aren’t successful because they don’t solve a real problem. People use products that fix a problem.</p>
<p>So for your next product (or hackathon idea), first find a problem, and then go build something to solve it!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Creating serverless Slack commands in minutes with Go & Up ]]>
                </title>
                <description>
                    <![CDATA[ By TJ Holowaychuk This post walks through the creation of a serverless Slack command written in Golang, and deployed to AWS Lambda in seconds with Up. You’ll be creating a /time <url> command used to check how long a website takes to respond. Up uses... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/creating-serverless-slack-commands-in-minutes-with-up-f04ce0cfd52c/</link>
                <guid isPermaLink="false">66c348220fa3812cdd5ea9ad</guid>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 11 Sep 2017 00:39:48 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*uFEp4Ubz5TOzlfo0-FE5Qw.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By TJ Holowaychuk</p>
<p>This post walks through the creation of a serverless Slack command written in Golang, and deployed to AWS Lambda in seconds with <a target="_blank" href="https://github.com/apex/up">Up</a>.</p>
<p>You’ll be creating a <code>/time &lt;url&gt;</code> command used to check how long a website takes to respond. Up uses your own AWS account. You can host a large number of custom apps for free while still utilizing the AWS free tier (1 million requests/m).</p>
<p>Check out the <a target="_blank" href="https://up.docs.apex.sh/">installation instructions</a> as well if you’re new to Up.</p>
<h3 id="heading-registering-the-slack-command">Registering the Slack command</h3>
<p>The first step is to create a Slack app, allowing you to register commands, among other things.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*dE4mvNpvma_IM0UHxlC4qQ.png" alt="Image" width="800" height="821" loading="lazy"></p>
<p>Once created, click “Slash commands” in the menu on the left, and register the <code>/time</code> command. You’ll need to keep this page open for a minute since we need a <strong>Request URL</strong> so Slack knows where to send requests.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*9Ot1sxpiFdpxDYe4xZMOGg.png" alt="Image" width="800" height="821" loading="lazy"></p>
<h4 id="heading-creating-the-slack-command">Creating the Slack command</h4>
<p>In your project’s directory create a file named <code>up.json</code>. Make sure to replace <code>PROFILE</code> with your AWS profile name (<a target="_blank" href="https://apex.github.io/up/#aws_credentials">read more</a>).</p>
<pre><code>{  <span class="hljs-string">"name"</span>: <span class="hljs-string">"slack-cmd-test"</span>,  <span class="hljs-string">"profile"</span>: <span class="hljs-string">"PROFILE"</span>}
</code></pre><p>Now we need a little HTTP server to process the Slack command POST request. Create a <code>main.go</code> file with the following net/http server.</p>
<p>Deploy it with <code>up</code> .</p>
<blockquote>
<p><strong>NOTE</strong> : The first deploy may take roughly 60s to set up resources.</p>
</blockquote>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*QuKc-ue1qJuwg9xt-NOlqQ.png" alt="Image" width="800" height="380" loading="lazy"></p>
<p>Now you need to grab the URL and paste it into the Slack command page so Slack knows where to send requests. Copy the command’s URL to the clipboard using:</p>
<pre><code>$ up url -cCopied to the clipboard!
</code></pre><p>Paste it in the <strong>Request URL</strong> field, then you’re good to give it a test run:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Ht5Cs6Wcfqezwk0ChfC84g.png" alt="Image" width="800" height="224" loading="lazy"></p>
<p>With any luck, you’ll see a Hello World response!</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*fFywF9gQ3XNKRe-QYxRHLw.png" alt="Image" width="800" height="195" loading="lazy"></p>
<h4 id="heading-performing-the-request">Performing the request</h4>
<p>Slack sends a POST request with form inputs, otherwise known as <code>application/x-www-form-urlencoded</code> (a tragically named mime type, turned standard-ish).</p>
<p>To access the form values, parse the form with the ParseForm() method. In this case all we need is the “text” field from r.Form, the parsed form.</p>
<p>Now that the request is portion is complete, import the <code>time</code> package and wrap the request with <code>time.Now()</code> and <code>time.Since()</code> to record the request duration.</p>
<p>Deploy again with <code>up</code> and immediately after the deploy you’re ready to test out the real version:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*WTFhC_dOxL3iqNTsQf6x3w.png" alt="Image" width="800" height="219" loading="lazy"></p>
<p>Two files and a few commands later, you’re done! Repeat for as many Slack commands as you need.</p>
<h3 id="heading-testing-locally">Testing locally</h3>
<p>One of Up’s strengths is deploying traditional “vanilla” HTTP servers. This means there is nothing new to learn when testing on your machine, develop the app as you always would.</p>
<p>Here’s an example of this application tested via <code>curl</code> :</p>
<pre><code>$ PORT=<span class="hljs-number">3000</span> go run main.go$ curl -d <span class="hljs-string">'text=https://apex.sh'</span> http:<span class="hljs-comment">//localhost:3000/</span>
</code></pre><pre><code>https:<span class="hljs-comment">//apex.sh took 19.33542m</span>
</code></pre><p>Hope that was helpful! Check out the <a target="_blank" href="https://apex.github.io/up/">documentation</a> for more help, and follow on <a target="_blank" href="https://twitter.com/tjholowaychuk">Twitter</a> for updates and various software rants.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Monitor Stack Overflow Activity in Slack ]]>
                </title>
                <description>
                    <![CDATA[ By Nicolas Grenié Powered by a complete serverless stack _Group of Developer Advocates watching developers coding — Credits: [Pixabay](https://pixabay.com/en/meerkat-snout-baby-mammal-guard-275967/" rel="noopener" target="blank" title=") Developer A... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/monitor-stack-overflow-activity-directly-into-slack-dc778913490f/</link>
                <guid isPermaLink="false">66c35b7bc7095d76345eafe7</guid>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ startup ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 10 Aug 2017 16:01:01 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*tWzWtTvWgglmNujgAREraA.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Nicolas Grenié</p>
<h4 id="heading-powered-by-a-complete-serverless-stack">Powered by a complete serverless stack</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/lcvnNk4qwdIdxJPmXh9hKD1vqzfnvSJpdExY" alt="Image" width="800" height="529" loading="lazy">
_Group of Developer Advocates watching developers coding — Credits: [Pixabay](https://pixabay.com/en/meerkat-snout-baby-mammal-guard-275967/" rel="noopener" target="<em>blank" title=")</em></p>
<p>Developer Advocates or Evangelists are often asked how they measure success.</p>
<p>One could argue that GitHub ⭐️’s on repositories are great. And Ash Hathaway shares her thoughts <a target="_blank" href="https://medium.com/@ash_hathaway/developer-evangelism-and-github-metrics-7e1c0a9d2fe2">here</a>.</p>
<p>Others could say that Stack Overflow questions give a good sense of an API popularity. I would agree with this statement.</p>
<p>If you don’t have a forum for your product, developers may end up asking questions directly on Stack Overflow.</p>
<p>I would not encourage companies to redirect all their support questions there or pushing for their brand tags, this is just bad. You are hijacking a community for your own needs instead of contributing to it.</p>
<h4 id="heading-but">But :</h4>
<p><strong>What happens when someone is posting a question there?</strong><br><strong>How do you get notified?</strong><br><strong>How can you react quickly?</strong></p>
<p>In my experience, you can only receive notifications when there are new questions on a particular tag. This might not cover all the questions posted about your API.</p>
<p>You can also use tools like <a target="_blank" href="http://mention.net/">Mention</a>. It covers more than Stack Overflow, but it’s not instantaneous.</p>
<p>To be more reactive, I hacked together something for our team at <a target="_blank" href="http://3scale.net/">3scale</a> a long time ago. I recently rewrote it using serverless technologies. It monitors questions about <strong>3scale</strong> and posts them on our <strong>#support</strong> Slack channel. The support team can jump on it and answer quickly.</p>
<p>I want to share this project, so you too can monitor Stack Overflow directly in Slack.</p>
<h3 id="heading-the-tools">The tools?</h3>
<p>To fit in 2017 trend, we will only use serverless technologies, which will make this tool free to use:  </p>
<ul>
<li><a target="_blank" href="https://aws.amazon.com/lambda/">AWS Lambda</a> to host the logic of our app  </li>
<li><a target="_blank" href="http://faunadb.com">FaunaDB</a> to store databases  </li>
<li><a target="_blank" href="http://serverless.com">Serverless Framework</a> to simplify deployments  </li>
<li><a target="_blank" href="http://slack.com">Slack</a> to be notified when a new question is asked</li>
</ul>
<h3 id="heading-the-logic">The logic⚙️</h3>
<p><a target="_blank" href="https://api.stackexchange.com/docs">Stack Exchange API</a> has an endpoint to search for questions. You can look for a term in tags, in the title, in the body of the question or all at the same time. In the example, we will search in all attributes.</p>
<p>You should create a key to access Stack Exchange API <a target="_blank" href="https://stackapps.com/apps/oauth/register">here</a>.</p>
<p>Our function will regularly call this endpoint to check if there are new questions posted. We will use the <em>Schedule Events</em> feature offered natively by Lambda, it’s a similar behavior as a cron job.</p>
<p>We will store the questions in a database to keep track of the ones we already sent to Slack and avoid duplicates.</p>
<p>If a Stack Overflow question is not in our database, we add it to the database and send details to Slack.</p>
<h3 id="heading-faunadb">FaunaDB</h3>
<p>I discovered <a target="_blank" href="http://faunadb.com">FaunaDB</a> a few months ago at <a target="_blank" href="http://gluecon.com">Gluecon</a>. They present themselves as the first <strong>serverless</strong> database engine. Everything is hosted on their end. FaunaDB is a globally distributed database that doesn’t require any provisioning. Capacity is metered and available on demand, so you only pay for what you use.</p>
<p>If you are familiar with <a target="_blank" href="http://firebase.com">Firebase</a>, you will recognize a similar data structure and routes to access resources. But it comes with more features, which makes it easier for example to query the database.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/oF0Mv4Xyrex1TMvUMFbYR41PiAaka1Qg7umn" alt="Image" width="800" height="381" loading="lazy">
<em>Example: How to create an entry in FaunaDB</em></p>
<p>For this app, you will need a database, with a <strong>questions</strong> class. We will also add an index <strong>questions_by_id</strong> on terms <strong>data</strong> and <strong>question_id.</strong> This will let us query the questions class by the Stack Overflow id.</p>
<p>If you are concerned about your database usage, you can add TTL to the <strong>questions</strong> class. This will automatically delete instances older than the TTL value.</p>
<p>Finally, you will need to create a server key for the questions class. This key will be used to authenticate our function to FaunaDB servers.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/xMXM3OayubBR1aEXJq1pcW1aO3CuPP5fdJMy" alt="Image" width="800" height="258" loading="lazy">
<em>Example: How to retrieve an entry in FaunaDB</em></p>
<h3 id="heading-slack">Slack</h3>
<p>To post to Slack, we would just need a simple incoming web hook. Create one <a target="_blank" href="https://my.slack.com/services/new/incoming-webhook/">here</a>.</p>
<h3 id="heading-configure-the-project">Configure the project</h3>
<p>Make sure you have installed <a target="_blank" href="http://serverless.com">Serverless</a> framework and configured the <a target="_blank" href="https://aws.amazon.com/cli/">AWS CLI</a> tool.</p>
<p>You can now clone this project locally.</p>
<pre><code>git clone git@github.com:picsoung/stackOverflowMonitor.gitcd stackOverflowMonitor
</code></pre><p>In <code>serverless.yml</code>you will modify the environment variables to your own values.</p>
<p><code>FAUNADB_SECRET</code>is the secret we created earlier to access FaunaDB<br><code>STACK_EXCHANGE_KEY</code> is the API key to access Stack Exchange API<br><code>SLACK_WEBHOOK_URL</code> is the URL of the Slack incoming webhook you’ve created<br><code>SLACK_CHANNEL</code> should be an existing channel name in your Slack team such as #support or #stackoverflow<br><code>SEARCH_KEYWORD</code> is the keyword you are interested in monitoring such as Node.js or Angular2</p>
<p>Once you have changed all the variables to your own values, we can test if everything works. We invoke the function locally with the following command:</p>
<pre><code>serverless invoke local — <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getStackOverflowQuestions</span></span>
</code></pre><p>As it’s the first time you are launching the function, it should post a message to your Slack channel. It should look like this:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/5rLOupAnNseTvLkWvGUBnjUDjMNsLVTwVubk" alt="Image" width="800" height="414" loading="lazy">
<em>How the notification should look like in your Slack channel</em></p>
<p>If you are happy with the result you can now deploy the function with the following command:</p>
<pre><code>serverless deploy
</code></pre><p>By default, the function is called every 20 minutes. You can customize it by changing the schedule property in <code>serverless.yml</code> file.</p>
<h3 id="heading-extend-the-project">Extend the project</h3>
<p>For now, we are only monitoring one term. You can launch multiple instances of this function to watch more terms or tags on Stack Overflow.</p>
<p>If you are interested in near to real-time solution, I encourage you to check <a target="_blank" href="http://streamdata.io/">Streamdata.io</a>. Their tools turn pulling API into streaming API.</p>
<p>If you want some nice dashboards that show how active your community is on Stack Overflow, I recommend <a target="_blank" href="http://keen.io/">Keen.io</a>. You can send all your Stack Overflow data there. Keen offers a <a target="_blank" href="https://keen.github.io/dashboards/">variety of libraries</a> to build beautiful dashboards.</p>
<p>We can also add more features in Slack, like buttons or menus. So people can claim a question or get assigned a question to answer.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>This was a small project that led me to discover how to use FaunaDB. Using AWS Lambda it’s way more efficient that the Heroku instance that I had in the past.</p>
<p>I hope you’ve found this serverless example useful. The code is <a target="_blank" href="https://github.com/picsoung/stackoverflowmonitor">open</a> on GitHub so feel free to contribute and add new features.</p>
<p>If you are working for a company that is selling to developers I am sure you already heard the “Be where the developers are.”</p>
<p>In the online world you have a good chance to find developers on websites like Hackers News, Stack Overflow, or GitHub. It’s important to measure what people say about your product or technologies on those sites.</p>
<p>This was a small project that led me to discover how to use FaunaDB. Using AWS Lambda is way more efficient than the Heroku instance that I had in the past.</p>
<p>I hope you’ve found this serverless example useful. The code is <a target="_blank" href="https://github.com/picsoung/stackoverflowmonitor">open</a> on GitHub so feel free to contribute and add new features.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Breaking The Fourth Wall In Software ]]>
                </title>
                <description>
                    <![CDATA[ By Alex Bunardzic Or, Everything Old Is New Again The phenomenon of breaking the fourth wall is well known in the world of theater and cinematography. The breaking of the so-called ‘fourth wall’ is typically brought about by one of the protagonists ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/breaking-the-fourth-wall-in-software-d08a25df34b7/</link>
                <guid isPermaLink="false">66c3463eeb0555cdb6fd9ac9</guid>
                
                    <category>
                        <![CDATA[ Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ UX ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 02 Mar 2016 01:24:12 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*9HDxFkXqLFXNlf6uaml_kQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Alex Bunardzic</p>
<h4 id="heading-or-everything-old-is-new-again">Or, Everything Old Is New Again</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*9HDxFkXqLFXNlf6uaml_kQ.jpeg" alt="Image" width="800" height="597" loading="lazy"></p>
<p>The phenomenon of <a target="_blank" href="http://www.mentorless.com/2013/06/10/breaking-the-fourth-wall-an-homage-to-a-storytelling-technique/">breaking the fourth wall</a> is well known in the world of theater and cinematography. The breaking of the so-called ‘fourth wall’ is typically brought about by one of the protagonists in the movie suddenly turning toward the camera and addressing the viewing audience, thus breaking the illusion that we’re witnessing a real-life event.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*-ZLMEgqztZiLT10beiju4w.png" alt="Image" width="800" height="449" loading="lazy"></p>
<p>But how does breaking the fourth wall work in software?</p>
<h4 id="heading-early-human-computer-interfaces">Early Human-Computer Interfaces</h4>
<p>The first computers were big and expensive and finicky. The way humans interacted with computers at that time was typically by feeding it a stack of <a target="_blank" href="https://en.wikipedia.org/wiki/Punched_card">punched cards</a>.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*BgavjyM1LTpWqUsQTWJsuA.jpeg" alt="Image" width="800" height="633" loading="lazy"></p>
<h4 id="heading-early-interfaces-were-intimidating">Early Interfaces Were Intimidating</h4>
<p>You obviously needed a university degree in order to operate computers.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*EUNOile3kX8oKD7IDha8yw.jpeg" alt="Image" width="580" height="435" loading="lazy"></p>
<h4 id="heading-early-interfaces-were-clunky">Early Interfaces Were Clunky</h4>
<p>A lot of buttons and switches and dials and levers. Intimidating and clunky.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*rS0cq9kXkYbb5efnPpUvRA.jpeg" alt="Image" width="590" height="479" loading="lazy"></p>
<h4 id="heading-breakthrough-text">Breakthrough — Text!</h4>
<p>Late 1960s — early 1970s witnessed the introduction of the so-called computer terminal. Emulating a typewriter for entering commands, and then displaying the results of the evaluated text on the monitor that looked like a TV screen.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*PN7kcdxanihRjkCkHq2ZyA.jpeg" alt="Image" width="800" height="710" loading="lazy"></p>
<h4 id="heading-text-is-intuitive">Text Is Intuitive</h4>
<p>Pretty much all people find text to be very intuitive — close to the way we think and speak. It feels much more natural to speak to the machine than it is to twoddle the knobs, flip the switches, and pull the levers (not to mention punch the cards or rewire the circuits).</p>
<h4 id="heading-but-computers-are-clunky">But Computers Are Clunky</h4>
<p>In the early days of computers, if you type a wrong command or use wrong syntax, the computer used to throw a tantrum. Temperamental beasts!</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*52wDsjDFZ1S5jv7daE8mMA.png" alt="Image" width="800" height="757" loading="lazy"></p>
<h4 id="heading-only-these-people-knew-how-to-talk-to-computers">Only These People Knew How To Talk To Computers</h4>
<p>You may recognize some faces on the group photo below.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*wRmlEjwtLIgpYLYYWqz4jg.jpeg" alt="Image" width="301" height="237" loading="lazy"></p>
<h4 id="heading-replace-text-with-graphical-interface-the-desktop-metaphor">Replace Text With Graphical Interface — The Desktop Metaphor</h4>
<p>Use pictorial representation to shield people from having to memorize awkward commands and syntax when operating computers. The idea was to present users with some familiar scenery — for example, their desktop. Everyone is familiar with the idea of having a desktop with file folders containing files and also a trash can at the side of the desk etc.</p>
<p>This Graphical User Interface (GUI) was deemed as being even more intuitive than text.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*CPQOwLViIbTIuxjKgj4P2w.png" alt="Image" width="672" height="480" loading="lazy"></p>
<h4 id="heading-guis-quickly-mushroomed-into-something-scary-and-non-intuitive">GUIs Quickly Mushroomed Into Something Scary and Non-Intuitive</h4>
<p>How is the interface below intuitive? It is as frustrating as the arcane and awkward syntax that the early computers insisted on when processing text.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*PjCaq0Jgh-jyAaasASxPpg.png" alt="Image" width="800" height="546" loading="lazy"></p>
<h4 id="heading-a-picture-is-worth-a-thousand-words">A Picture Is Worth A Thousand Words</h4>
<p>True. But what if most of those thousand words are gibberish? What’s the worth of that?</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*0rWYGl7qH1FMgXTRIQXGbQ.png" alt="Image" width="800" height="510" loading="lazy"></p>
<h4 id="heading-bottom-line-people-find-guis-frustrating">Bottom Line: People Find GUIs Frustrating</h4>
<p>GUIs typically present us with too much information at once. Then the onus is on us to digest all that and try to make sense out of it.</p>
<p>GUIs also tend to enforce ‘one size fits all’ approach, which is not very user-centric.</p>
<h4 id="heading-so-whats-the-solution-then">So What’s The Solution Then?</h4>
<p>What if, instead of this poorly thought out buffer that consists of intermediary graphical representation, we were to revert to <em>plain text</em> again? It is, after all, much easier to focus and follow simple discussion threads than it is trying to navigate hairy, convoluted GUIs.</p>
<h4 id="heading-but-computers-are-brittle-and-will-not-be-as-forgiving-as-guis">But Computers Are Brittle And Will Not Be As Forgiving As GUIs</h4>
<p>We have grown to depend on GUIs as we would depend on training wheels. Installing training wheels gives us a sense of safety — we cannot fall, and yet we can somehow move forward and get to our destination.</p>
<h4 id="heading-cant-get-very-far-using-training-wheels">Can’t Get Very Far Using Training Wheels</h4>
<p>Training wheels are okay for peddling around our back yard, but we can’t use them effectivelly in real life situations.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*vEw3R-I2eSOpR3-jSLHW0Q.jpeg" alt="Image" width="252" height="200" loading="lazy"></p>
<h4 id="heading-how-to-remove-training-wheels-and-learn-to-ride-properly">How To Remove Training Wheels And Learn To Ride Properly?</h4>
<p>Break the fourth wall!</p>
<p>How do we break the fourth wall? Stop pushing pixels!</p>
<p>How can we stop pushing pixels?</p>
<h4 id="heading-frustrating-example">Frustrating Example</h4>
<p>Say we order something online. The next week we may be wondering about the status of our order (hasn’t, for some reason, arrived yet). Frustrated, we open the browser, go to the online store, log in, and then try to navigate to the <em>order status</em> page.</p>
<p>To reach the <em>order status</em> page, we have to navigate through the veritable jungle of confusing menus, shifting layouts, poorly styled links (often barely visible on the page), and so on. To add insult to an injury, these elements keep constantly changing, so we cannot rely on our muscle memory from previous navigation sessions.</p>
<h4 id="heading-less-frustrating-example">Less Frustrating Example</h4>
<p>What if, instead of doing all of the above gymnastics and acrobatics, we just do the following:</p>
<p>Go to the command line (say in <a target="_blank" href="https://www.messenger.com">Messenger</a> or <a target="_blank" href="https://slack.com">Slack</a> etc.) and type ‘_@merchant<em>name what’s the status of my order?</em>’</p>
<p>That way, we let the merchant service (i.e. Amazon or Etsy or Ebay etc.) do the legwork on our behalf.</p>
<p>Which of the two ‘check order status’ experiences is more intuitive?</p>
<h4 id="heading-guess-what-we-just-broke-the-fourth-wall-in-software">Guess What — We Just Broke The Fourth Wall In Software!</h4>
<p>By foregoing the GUI, we have switched to interacting with some online service using plain text. And it felt quite natural. Notice how, by doing that, we were not expected to undergo any training.</p>
<p>How’s that possible? Simple — instead of interacting with the bare metal computing machinery, we got in touch with a sophisticated <a target="_blank" href="https://www.chatbots.org/chatbot/">chatbot</a> whose role is to know how to parse and interpret colloquial English text.</p>
<h4 id="heading-how-do-we-get-involved-with-a-chatbot">How Do We Get Involved With A Chatbot?</h4>
<p>We <em>/invite</em> it to our channel. For example, say we find out there is a chatbot that specializes in restaurant recommendations. We wish to get in touch with that bot, and after finding out the name of the bot (say, <em>restobot</em>), we ‘hire’ that bot to work for us by typing:</p>
<p><em>/invite @restobot</em></p>
<p>Once invited to your channel, this bot will remain always online, listening attentively for its name to get mentioned.</p>
<h4 id="heading-the-bot-is-the-buffer">The Bot Is The Buffer</h4>
<p>Similar to how a GUI was the buffer between us, human users, and the cold, bare computing machinery, bots are now replacing GUIs as that warm and fuzzy buffer. Bots are shielding us from having to deal with the temperamental machinery by translating our plain English commands into something that the underlying computing services can understand and work with.</p>
<h4 id="heading-whats-the-value-proposition-of-bots">What’s The Value Proposition Of Bots?</h4>
<p>Bots are <strong><em>attentive to human needs</em></strong> and <strong><em>sensitive to human frailty</em></strong>.</p>
<h4 id="heading-so-is-this-a-revolutionary-change">So Is This A Revolutionary Change?</h4>
<p>Not really. It’s the natural outcome of the advancements we’ve made in the field of human-computer interaction. So it’s more of an evolutionary change.</p>
<p>In actuality, this conversation-based interface is not all that different from operating computers via GUIs. Because, if we examine a bit closer what’s going on behind the surface of a typical GUI processing, we’ll find the following scenario:</p>
<ul>
<li>A user wants to ask the computer to do something</li>
<li>User goes to the screen/page where they get presented with one or more <em>input fields</em></li>
<li>These input fields, sometime referred to as <em>text boxes</em>, accept text from the user</li>
<li>The GUI then listens to the user’s gestures, such as ‘send’ or ‘submit’ gesture</li>
<li>Once the event signalling the expected gesture occurs, the GUI turns around and sends <strong><em>text</em></strong> to the underlying servers</li>
</ul>
<h4 id="heading-guis-are-also-text-driven">GUIs Are Also Text-Driven</h4>
<p>Similar to how bots operate, GUIs also posses the knowledge of how to collect text from users and then formulate the collected values using the strict syntax that the back-end computers can understand.</p>
<p>So if that’s the case, where do pixels come into play?</p>
<p>Most of the time, pixels are being used as <em>decoration</em>. They typically sugarcoat the screen, or a web page, and dress it up in a robe that looks more familiar to the users. Such as, for example, dressing up a web form to look similar to the paper form.</p>
<p>Doing that decompresses the tension users may feel when attempting to work with computers. The intent is to demystify the interaction, and make it feel similar to everyday interactions one may encounter when dealing with various non-virtual services.</p>
<h4 id="heading-remove-pixelated-decorations-and-what-are-we-left-with">Remove Pixelated Decorations, And What Are We Left With?</h4>
<p>One word — <em>microcopy</em>.</p>
<p>What is <em>microcopy</em>?</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*j7jhl0iiIYca_3vIj5LuyA.png" alt="Image" width="800" height="161" loading="lazy"></p>
<p>In the above example, <em>microcopy</em> is any text placed next to the GUI control. In a GUI form, we may ask users to enter their phone number. Often times, people are not sure if they want to do that, and also why would we need their personal information? So we place a simple, direct sentence in the brackets, right next to the caption asking for the phone number, explaining the purpose of that request. For example, “we need your phone number for shipping-related questions”.</p>
<p>Or, we may offer a <em>microcopy</em> that is a bit more verbose, such as in the example below:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*wbsLLL8TwN9EHY9w0PiVZQ.png" alt="Image" width="658" height="176" loading="lazy"></p>
<h4 id="heading-conversational-thread">Conversational Thread</h4>
<p>If we imagine removing all pixels and with it the graphical user interface, what we’re left with is a simple <em>conversational thread</em> that gets recorded between the user and the computer.</p>
<h4 id="heading-what-are-the-advanatages-of-conversational-interfaces">What Are The Advanatages Of Conversational Interfaces?</h4>
<ul>
<li>Intuitive</li>
<li>Sensitive to human frailty (the bot will try to clarify human request if initially not clearly understood)</li>
<li>Familiar (everyone is already fully accustomed to chatting with family/friends/coworkers)</li>
<li>Consistent experience across all devices (immune to any concerns/issues related to layouts, fonts, colors, etc.)</li>
<li>Guarantees full user ownership of the conversation — fully personalized discussion thread is forever recorded and owned by the human user (full transparency, complete audit)</li>
</ul>
<h4 id="heading-conversational-commerce">Conversational Commerce</h4>
<p>As we’re moving into the post-web 2.0 world, the universal slogan ‘content is king’ now becomes ‘commerce is king’. In the web 2.0 world, when a user completes a transaction via GUI, all the steps that transpired between the user and the online service may have been recorded by the back-end service, but are opaque to the user. In the world of <em>conversational commerce</em>, every step that transpired between the user and the online service is recorded in the conversational thread and is fully owned by the user.</p>
<h4 id="heading-conversational-interface-experience-is-similar-to-regular-customer-support-experience">Conversational Interface Experience Is Similar To Regular Customer Support Experience</h4>
<p>Similar to how calling a 1–800 number was a mainstream customer support channel before the emergence of web 2.0 and the mobile apps, we’re moving back into conversing directly with the customer suport. Only this time, instead of being put on an indefinite hold and forced to listen to horrible muzak, we’re conversing with bots which are always on and are much faster and more accurate, more detialed than human workforce.</p>
<p>And same as with the 1–800 scenarios, if our call for some reason cannot get resolved in satisfactory manner, we can easily <em>escalate</em>. In the old regime we would ask the customer service representative to talk with their supervisor, and in the new regime we would ask the bot-agent to put us in touch with the human operator.</p>
<h4 id="heading-lets-create-our-own-bot-now">Let’s Create Our Own Bot Now!</h4>
<p>Perhaps the best way to grasp this transition from graphical to text-based user interface is to roll up our sleeves and create a bot from ground up. Creating a bot is quite easy, because the tools necessary for building bots have been largely commoditized. Still, I feel that merely creating a bot would not be an efficient nor convincing demonstration of the importance of conversational commerce. That’s why I’m proposing that we here learn not only how to create a bot, but also how to create a bot that is capable of doing something useful for us.</p>
<p>For example, let’s create a bot that will help us get in touch with some e-commerce service by using plain text as the user interface.</p>
<h4 id="heading-create-online-commerce-service-first">Create Online Commerce Service First</h4>
<p>For the sake of brevity, let’s create a simple e-commerce site that will host an inventory of products. Those products will be offered for sale, and some of the products on sale will also be offered at a discount price.</p>
<p>We will use state-of-the-art web development framework (<a target="_blank" href="http://rubyonrails.org">Ruby on Rails</a>) for building this service. If you don’t have the Rails framework installed, please refer to the Rails site for instructions on how to get it installed on your computer.</p>
<p>Once installed, we use Rails to create a new site. Open the terminal and type:</p>
<blockquote>
<p>rails new your_site_name</p>
</blockquote>
<p>Rails will then create the new project for you, and once you navigate to your new project (by typing _cd your_site<em>name</em>), you are ready to create the inventory of products to be hosted on the new site. We will create a resource called Product, and will then assign several attributes to it:</p>
<blockquote>
<p>rails generate scaffold Product name:string price:decimal on_special:boolean discount_percentage:integer description:text</p>
</blockquote>
<p>The above command will create the resource called Product and will implement product attributes, such as product name, its price, whether or not it’s on special, and the discount percentage.</p>
<p>Now is the time to create a database where the inventory of products will get stored. We do that by using the specifications that got created with the previous command. The command to create and install the <em>products database</em> is as follows:</p>
<blockquote>
<p>rake db:migrate</p>
</blockquote>
<p>The only thing left to do is to start the server and verify that the web site we’ve just created is working as expected:</p>
<blockquote>
<p>rails s</p>
</blockquote>
<h4 id="heading-maintain-inventory-of-products">Maintain Inventory Of Products</h4>
<p>Now that we’ve created our products database and our web site, we should navigate to it and add some products. Open up the web browser and navigate to the <em>http://localhost:3000/products</em> URL.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*PmHQZb1yOtPz_zXyVVr1mw.png" alt="Image" width="800" height="255" loading="lazy"></p>
<p>Of course, the product inventory page will be empty, because we haven’t added any products yet. Let’s do that by clicking on the ‘New Product’ link.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*RfJg9_LeX-gmkYKWQ0vEcg.png" alt="Image" width="434" height="836" loading="lazy"></p>
<p>After entering some values, we click on the “Create Product” button and the product is now added to the inventory. Let’s enter a few more products (remembering to click on the “On special” checkbox for some of them).</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*YDC7lrPbVtgGOjDBVVLJYQ.png" alt="Image" width="800" height="342" loading="lazy"></p>
<p>Now that we have several products in our inventory, time to build a conversational commerce bot. What will be the usefulness of that bot? In order to keep things simple, we will endow this bot with the ability to answer text commands enquiring about the products that are on special.</p>
<h4 id="heading-where-will-our-bot-live">Where Will Our Bot Live?</h4>
<p>A bot must be able to listen to text messages arriving from users, and the best way to make that happen is to add the bot to some messaging platform. Currently, the most attractive messaging platform for adding bots is <a target="_blank" href="https://slack.com">Slack</a>, so we’re going to use it to demonstrate how to build conversational commerce.</p>
<p>Signup with Slack (if you’re not a member already), and then go to:</p>
<blockquote>
<p><a target="_blank" href="https://yourteam..slack.com/services/new/bot">https://yourteam.slack.com/services/new/bot</a></p>
</blockquote>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*IJ5jy-PksVLWxlxTUSp5EQ.png" alt="Image" width="800" height="442" loading="lazy"></p>
<p>You will be asked to specify the name of your bot. Let’s call our bot ‘gofer’.</p>
<p>After clicking the “Add bot integration” button, we will be able to set our gofer up on Slack. First thing first, let’s choose the icon that will represent our bot. I choose my favourite robot, Bender.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*lpkWqgLIBMbpquZn-1b6lA.png" alt="Image" width="800" height="507" loading="lazy"></p>
<p>We can also add bot’s first and last name and a description outlining bot’s capabilities.</p>
<p>After saving the integration, we notice the API Token; this token is extremely important, as it allows the integration between our hand crafted bot and the Slack platform. Let’s copy the value of that API Token for future reference.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*fUUgAoumOA5Dwc_AmJd2ZA.png" alt="Image" width="800" height="161" loading="lazy"></p>
<h4 id="heading-final-step-crafting-our-bot">Final Step — Crafting Our Bot</h4>
<p>Now’s the time to open the source code of our e-commerce product inventory site. We must add bot to this site, because the bot will be able to utilize the services built into our inventory site and aswer the questions coming from the Slack users.</p>
<p>First thing we need to do is navigate to the <em>config</em> folder in our <em>product inventory</em> site and create a new file. That file will contain the Slack API Token. We can name this file anything we want; I prefer to keep its name simple, so I call it <em>api.rb</em>. This file will consist of only one line of code:</p>
<blockquote>
<p>ENV[‘SLACK_API_TOKEN’]=’xoxb-23830295172-r5CzhzDnUSZQfUfXWmR’</p>
</blockquote>
<p>Next we need to tell Rails framework to load that API Token during the intialization phase. We open the <em>config/environment.rb</em> file, and add the following two lines of code:</p>
<blockquote>
<p>api = File.join(Rails.root, ‘api.rb’)<br>load(api) if File.exists?(api)</p>
</blockquote>
<p>Now that we have declared Slack API Token and instructed Rails to load it, we need to add our bot to the project. The best way to do that is to navigate to the <em>app</em> folder, and create a new folder simply named <em>bots</em>.</p>
<p>Create a new file in the <em>app/bots</em> folder, and name it _real_time<em>messaging.rb.</em> This file will deal with the thread used for our bot to listen for the incoming messages. Add these lines to the file, and save it:</p>
<blockquote>
<p>$:.unshift File.dirname(<strong>FILE</strong>)</p>
<p>Thread.abort_on_exception = true</p>
<p>Thread.new do<br> Gofer.run<br>end</p>
</blockquote>
<p>You have probably noticed that in the file above we have mentioned <em>Gofer</em>; we managed to get ahead of ourselves by mentioning the bot we havent created yet. But that’s okay, because we’re not ready yet to kick start the bot service listening on the channel.</p>
<p>So the real challenge now is to figure out how to craft our bot named Gofer. For the sake of brevity, we will cheat here by leveraging the commodity service known as <a target="_blank" href="https://github.com/dblock/slack-ruby-bot">Slack Ruby Bot</a>. Leveraging this commodity allows us to save time that would’ve otherwise be spent on coding the low level web sockets processing, which is a fairly involved exercise.</p>
<p>The quickest way to leverage this commodity is to open the <em>Gemfile</em> found in the root of the project, and add the following line to it:</p>
<blockquote>
<p>gem ‘slack-ruby-bot’</p>
</blockquote>
<p>Save the file and then go to the command line to the root of the project and run:</p>
<blockquote>
<p>bundle install</p>
</blockquote>
<p>When the installation completes, will will have baked in our Slack Ruby Bot commodity service, which we will leverage when creating our <em>Gofer</em> bot.</p>
<p>But before we jump into crafting the bot logic, we need to complete one more step related to the underlying plumbing needed for the Slack bot to work properly. Navigate to the <em>config/initializers</em> folder, and create a new file simply called <em>bot.rb</em>. This is a simple file consisting of only one line of code:</p>
<blockquote>
<p>require File.join(Rails.root, ‘app/bots/real_time_messaging’)</p>
</blockquote>
<p>It is simply instructing Rails to load the _real_time<em>messaging.rb</em> file on initialization. And if we look back into the contents of the _real_time<em>messaging.rb</em> file, we will see that, once the web site boots, it will also run a thread that is responsible for running the <em>Gofer</em> bot.</p>
<p>And finally, on to creating the bot logic! Create a new file in the <em>app/bots</em> folder, and name it <em>gofer.rb</em>. This file will declare <em>Gofer</em> bot as inheriting its capabilities from the commodity we’ve just installed — <em>SlackRubyBot::Bot</em>.</p>
<blockquote>
<p>class Gofer&lt; SlackRubyBot::Bot</p>
</blockquote>
<p>This bot inherits some rudimentary capabilities from <em>SlackRubyBot,</em> such as the ability to respond to commands. And these commands is what we’ll be teaching this bot, telling it how to respond to each command it receives.</p>
<p>Let’s start with something extremely simple — let’s teach our <em>Gofer</em> bot how to handle the ‘help’ command. Add the following command definition to the <em>gofer.rb</em> file:</p>
<blockquote>
<p>command ‘help’ do |bot, thread|<br> bot.say(channel: thread.channel, text: “Help is on its way.”)<br>end</p>
</blockquote>
<p>This command is going to use the bot to get it to display the text ‘Help is on its way.’ to the channel from where it was asked for help.</p>
<p>Save the file, go to the command line and start the server (<em>rails s</em>). You will now notice additional messages on the command line when the server starts:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*OQIUlhZZXjXDj1nGPNZ3rQ.png" alt="Image" width="800" height="42" loading="lazy"></p>
<p>Now that our bot <em>Gofer</em> is successfully connected to our Slack team, we can test it. Go to your Slack team, and you will see that <em>gofer</em> bot is online (there is a green semaphore ligh next to its name). Click on its name, and then type ‘help’. You will see that the bot immediately responds with the text we’ve given it above.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*rWMVuPBMqRqAeZqHFBD_Hw.png" alt="Image" width="800" height="504" loading="lazy"></p>
<p>OK, neat, so now we see that our bot is working. But how do we get it to tell us what products are offered on special at the moment? Simple — we just add a new command (let’s call it ‘promo’ for simplicity) and instruct the bot to gather information on the products with discounted prices and send us the list back.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Tey80HTwj1fi2aebg1DSFA.png" alt="Image" width="800" height="165" loading="lazy"></p>
<p>Save the file, restart the server, and flip over to Slack to ask gofer what’s on special.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*yeQLpX2AXEFRpWmUo_osuA.png" alt="Image" width="692" height="316" loading="lazy"></p>
<p>Just to verify that the bot is indeed working in real time, go to the product inventory and make some changes. Like, remove the discount for the Hat and maybe add discount for some other product. After you do that and ask gofer what’s on promo, it will tell you all the latest details.</p>
<h4 id="heading-conclusion">Conclusion</h4>
<p>Latest trends have shown that more and more people tend to spend majority of their online time chatting. As of the early 2016, almost 1 billion active users are spending time on Messenger and other chat apps. As people are getting familiar with texting with their friends, family, and coworkers, they are also slowly becoming acclimatized to chatting with bots. That experience is offering a more intuitive way to get things done, and all signs indicate that this new way of interacting with computers is what people at large seem to prefer.</p>
<p>We have attempted to illustrate how will this transition work by walking the readers through the hands-on session on how to create their own bots. Once the first trial bot gets created, we realize that sky is the limit — there are so many useful things these bots can do, so let’s get cracking!</p>
<h4 id="heading-update">Update</h4>
<p>I was invited by the <a target="_blank" href="http://www.redacademy.com">RED Academy</a> to deliver the talk on Conversational Commerce and the Bot ‘revolution’. The talk was recorded and can be viewed below:</p>
<p><em>Intrigued? Want to learn more about the bot revolution? Read more detailed explanations here:</em></p>
<p><a target="_blank" href="https://medium.com/bots-for-business/how-to-build-a-stateful-bot-a2703ff2d57b#.szcp08ze5">How To Build A Stateful Bot</a><br>The Age of Self-Serve is Coming to an End<br><a target="_blank" href="https://medium.com/bots-for-business/https-medium-com-alexbunardzic-only-no-ux-is-good-ux-c24a7cbd12f4#.aqpbs89oj">Only No Ux Is Good UX</a><br><a target="_blank" href="https://medium.com/bots-for-business/stop-building-lame-bots-b093dcd5f28b#.c3k9kcprv">Stop Building Lame Bots!</a><br><a target="_blank" href="https://chatbotsmagazine.com/four-types-of-bots-432501e79a2f#.9tuz1winx">Four Types Of Bots</a><br><a target="_blank" href="https://chatbotsmagazine.com/is-there-a-downside-to-conversational-interfaces-55bed7220c2f#.l43a0r4j4">Is There A Downside To Conversational Interfaces?</a><br><a target="_blank" href="https://medium.com/@alexbunardzic/are-bots-just-a-fad-are-guis-really-superior-a1f52007d2b9#.a7zvp7kx2">Are Bots just a Fad? Are GUIs really Superior?</a><br><a target="_blank" href="https://medium.freecodecamp.com/how-to-design-a-bot-protocol-4b7584fc8d2c#.3d7xy2g5v">How to Design a Bot Protocol</a><br><a target="_blank" href="https://medium.com/bots-for-business/bots-are-the-anti-apps-869639cfa179#.gf5x3rw22">Bots Are The Anti-Apps</a><br><a target="_blank" href="https://medium.com/bots-for-business/how-much-nlp-do-bots-need-a9fd55d64094#.9r83gcpve">How Much NLP Do Bots Need?</a><br><a target="_blank" href="https://medium.com/bots-for-business/screens-are-for-consumption-not-for-interaction-6151fb8db6d7#.4qh22p38n">Screens Are For Consumption, Not For Interaction</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Unfundable Slack bots ]]>
                </title>
                <description>
                    <![CDATA[ By Bertrand Fan In December, Slack announced a $80 million fund to invest in software projects that complement its technology. As an early adopter of the Slack API, here are some “bets” that I’ve made on the Slack platform: A bot that plays all of St... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/unfundable-slack-bots-9369a75fdd/</link>
                <guid isPermaLink="false">66c36403b737bb2ce70731fc</guid>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ humor ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ star wars ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 01 Mar 2016 16:32:06 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*KUlXltNVajILJHiucE6sHg.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Bertrand Fan</p>
<p>In December, Slack announced a $80 million fund to invest in software projects that complement its technology. As an early adopter of the Slack API, here are some “bets” that I’ve made on the Slack platform:</p>
<h4 id="heading-a-bot-that-plays-all-of-star-wars-despecialized-edition-han-shoots-first-one-frame-every-ten-seconds-it-takes-about-20-hours-to-get-through-the-whole-thing">A bot that plays all of Star Wars Despecialized Edition (Han shoots first!) one frame every ten seconds. It takes about 20 hours to get through the whole thing.</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*KUlXltNVajILJHiucE6sHg.png" alt="Image" width="510" height="861" loading="lazy"></p>
<h4 id="heading-a-bot-that-hosts-games-of-the-resistance-optionally-with-the-assassin-module-from-the-hidden-agenda-expansion-secret-voting-is-conducted-via-dm-with-the-bot-while-public-actions-are-done-in-the-channel">A bot that hosts games of The Resistance (optionally with the Assassin Module from the Hidden Agenda expansion). Secret voting is conducted via DM with the bot, while public actions are done in the channel.</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*mrfae1_an3lG1l9BxOBoyA.png" alt="Image" width="542" height="845" loading="lazy"></p>
<h4 id="heading-a-bot-that-lets-you-play-wolfenstein-3d-by-issuing-commands-left-right-up-down-open-and-fire-you-can-optionally-specify-how-many-degrees-to-turn-left-and-right-but-by-default-you-turn-45-degrees-ive-never-successfully-completed-the-first-level-but-i-have-managed-to-kill-several-guards">A bot that lets you play Wolfenstein 3D by issuing commands (left, right, up, down, open, and fire). You can optionally specify how many degrees to turn left and right, but by default you turn 45-degrees. I’ve never successfully completed the first level, but I have managed to kill several guards.</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*ZZSwPJsD1p3xiyHJzP9HMQ.gif" alt="Image" width="457" height="825" loading="lazy"></p>
<p>I’m happy to announce that I’ve completed my latest bot, <a target="_blank" href="https://vandelayindustries.online/">Vandelay Industries</a>. Vandelay Industries is a shameless rip-off of <a target="_blank" href="https://frinkiac.com/">Frinkiac</a> that lets you search for animated gifs from every episode of Seinfeld in Slack. It has every line of dialogue ever spoken in Seinfeld.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*dF_ekpm0NzefyQJBIw07ww.gif" alt="Image" width="457" height="367" loading="lazy"></p>
<p>If you’d like to try it out in your Slack team, just go to <a target="_blank" href="https://vandelayindustries.online">Vandelay Industries</a> and click <strong>Add to Slack</strong>. It will ask you for permission to add a new slash command, /vandelay and once authorized, it should be ready to use immediately within Slack.</p>
<p>If you’re interested in the technical details of how it works, keep on reading! If not, you’ve managed to beat the estimated reading time for this article. Congratulations!</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*UHa3mQHcgfao8vYc7MThUw.gif" alt="Image" width="320" height="180" loading="lazy"></p>
<h4 id="heading-processing">Processing</h4>
<p>I initially started with 91G of all nine seasons of Seinfeld in surprisingly high quality 720p. I ultimately ended up with 111G worth of animated GIFs which tells you everything you need to know about the efficiency of that file format. This section is all about how I went from one to the other.</p>
<p>The files were in MKV containers, so I was able to use <a target="_blank" href="https://mkvtoolnix.download/">MKVToolNix</a> to extract the subtitles from them. You can use mkvinfo to list the various segment tracks:</p>
<pre><code>mkvinfo Seinfeld.S01E01.The.Seinfeld.Chronicles.mkv
</code></pre><p>It’s a little hard to read, but the track that we’re interested in is Track number 3, the first subtitles track. After noting the track ID (2), we can extract the subtitles into an SRT file like this:</p>
<pre><code>mkvextract tracks Seinfeld.S01E01.The.Seinfeld.Chronicles.mkv <span class="hljs-number">2</span>:S01E01.srt
</code></pre><p>The SRT format is fairly straightforward. It contains a counter, start time, end time, and the subtitle text. Using a parser like <a target="_blank" href="https://www.npmjs.com/package/subtitles-parser">subtitles-parser</a>, we can easily iterate over the subtitles.</p>
<p>The next step is to loop through each subtitle and extract that time range from the MKV into an animated GIF. There’s an <a target="_blank" href="http://blog.pkh.me/p/21-high-quality-gif-with-ffmpeg.html">excellent article about using ffmpeg to encode high quality GIF</a> but if you don’t want to read that right now, the trick is to extract a specialized palette from the section of the video that you’re interested in and then use that to encode the GIF.</p>
<p>Here’s a script that I adapted for the purposes of this processing step:</p>
<p>320 refers to how many pixels wide the resulting GIF will be. You’ll notice that instead of specifying a start time and an end time, I instead specify a duration. Although ffmpeg claims to support end times, no matter which version I tried I could not get it to properly extract the right range, so I ended up calculating the duration by subtracting the start time from the end time and abusing the unix epoch like this:</p>
<p>After applying the gifenc.sh script, we’re left with a nice animated GIF of the correct extracted range like this:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*1rukLpm7GTaNxkunKTjiSg.gif" alt="Image" width="320" height="180" loading="lazy"></p>
<p>But I wanted to display the subtitle text at the bottom of the GIF and after digging through the ImageMagick documentation, I came up with this:</p>
<p>It’s not the most elegant solution, but it gets the job done. Our final gif looks like this:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*ZTBtKosGpleB6sf_VVpvxA.gif" alt="Image" width="320" height="180" loading="lazy"></p>
<p>Now just do that about 104,782 more times and you’re done. Running an entire episode through ffmpeg and ImageMagick took about 30 minutes on my top-of-the-line-in-2011 Macbook Pro. This is the part of the story where I’d like to tell you that I managed to leverage Amazon Elastic Transcoder or made a hadoop job to distribute the load to all the computers in my house, but really what I did was put all of this on my <a target="_blank" href="https://www.soyoustart.com/">reconditioned budget OVH server</a> and let it run for 5 days while I continued to live my life.</p>
<p>Once it was done encoding all the GIFs, I just let Apache serve them statically with a long Expires header and put Cloudflare in front of the whole domain. Only time will tell if that will actually hold up with traffic demands.</p>
<h4 id="heading-searching">Searching</h4>
<p>I installed Elasticsearch and indexed the contents of the SRT files. Here’s where I encountered some non-technical snags: In Season 6, episodes 14 and 15 are an hour-long clip show called Highlights of a Hundred where Jerry Seinfeld shows you a bunch of old clips from previous episodes. And in the final episode of Seinfeld, Season 9, Episode 23, they do a ton of flashbacks to previous episodes. Both of these would routinely get returned in the search results, so I just excluded them from the search query. There’s probably a better way to just lower the quality of those episode scores, but the documentation for doing so in Elasticsearch is about as easy to read as the above mentioned ImageMagick documentation. And at the end of the day, no one wants to see clips from either of those two episodes. Sorry, Larry David, the last episode was terrible.</p>
<h4 id="heading-slash-command">Slash Command</h4>
<p>The final step was just to wrap it all together with a Slack slash command, which is just a simple Express app that acts as a client to the Elasticsearch instance. There’s some OAuth to package the command as a Slack App and handle the Add to Slack button, but I don’t really need to check for authentication when the requests come through so I’m not saving the authorization tokens. The code for the server is available here: <a target="_blank" href="https://github.com/bertrandom/vandelayindustries-slack-server">vandelayindustries-slack-server</a>.</p>
<p>That’s it! I hope this technical writeup helps the next person that wants to extract GIFs from an entire television show for little to no actual reason.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*gQJzoNu5gkQxG9EOBDiF3w.gif" alt="Image" width="320" height="180" loading="lazy"></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
