<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nabheet Madan</title>
    <description>The latest articles on DEV Community by Nabheet Madan (@nabheet).</description>
    <link>https://dev.to/nabheet</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F87907%2Fa5b2295e-71df-4d2e-8c62-9236ba42602a.jpg</url>
      <title>DEV Community: Nabheet Madan</title>
      <link>https://dev.to/nabheet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nabheet"/>
    <language>en</language>
    <item>
      <title>Crazy case of Embedding Git Repositories</title>
      <dc:creator>Nabheet Madan</dc:creator>
      <pubDate>Wed, 16 Jun 2021 10:04:57 +0000</pubDate>
      <link>https://dev.to/nabheet/crazy-case-of-embedding-git-repositories-297</link>
      <guid>https://dev.to/nabheet/crazy-case-of-embedding-git-repositories-297</guid>
      <description>&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;We were having multiple projects in a folder and all pointing to its own remote git repo. we thought of making our parent folder also available on GitHub,so that all projects are  available at one place. We went ahead with our typical regular follow as mentioned below &lt;/p&gt;

&lt;p&gt;Initializing the parent folder with below mentioned command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then as usual staging all content and commiting using below mentioned command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Git add .
Git commit  -m “Main Project”
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Created a repo on Github and added the remote connection uploaded the stuff.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git branch -M main
git remote add origin &amp;lt;url&amp;gt;
git push -u origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We were expecting everything to be available on remote repo but sadly that is where the real problem starts. If you look at screenshot below we can see two projects but none of them is showing any contents. So in this post we will try to demystify and resolve this situation by learning something new.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8bb55q1dijoo9jaflw03.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8bb55q1dijoo9jaflw03.PNG" alt="Situation we are in"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For people who like to watch instead of reading can checkout this video&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ZNVWVytzCZw"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Journey towards finding the solution.
&lt;/h2&gt;

&lt;p&gt;We were surprised by this, so thought of checking again the steps executed so far. That is where we got our first hint. It was a warning message and as always, we have ignored it.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgley8h7i2ard8uvjf00l.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgley8h7i2ard8uvjf00l.PNG" alt="Warning Message"&gt;&lt;/a&gt;&lt;br&gt;
It clearly highlights in case you have repo embedded in repo it will not upload all the contents of the inner repo. Good thing is it also leads you into a direction of a concept which is called as &lt;a href="https://git-scm.com/book/en/v2/Git-Tools-Submodules" rel="noopener noreferrer"&gt;Submodules&lt;/a&gt;. Submodules are nothing they allow you to have repo inside repo. Normally while developing project we tend to refer multiple external libraries, which themselves are versioned one. So in order to use those the concept of submodule was brought in.  So here lie the answer to our problem.&lt;/p&gt;

&lt;p&gt;So as a first step we need to add the submodules to our parent class. Lets do it using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Git submodule add &amp;lt;url&amp;gt; &amp;lt;name of module&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have added our both projects as submodules, and you can see a new  file .gitmodules is created. On checking its content it contains details of all the submodules.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsyssj1xfrw4ntjxl8up.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsyssj1xfrw4ntjxl8up.PNG" alt="submodules"&gt;&lt;/a&gt;&lt;br&gt;
Once that is done now we follow our regular commit cycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Git add .
Git commit  -m “Main Project”
git branch -M main
git remote add origin &amp;lt;url&amp;gt;
git push -u origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets see how does the remote repo looks like. Wow it contains an extra link which point to the other repositories&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhsc0uaswzyf61ggsok36.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhsc0uaswzyf61ggsok36.PNG" alt="Correct Repo"&gt;&lt;/a&gt;&lt;br&gt;
Okay so our core issue is resolved but we thought of exploring it further as we found this concept very powerful.&lt;/p&gt;
&lt;h2&gt;
  
  
  Exploring Further
&lt;/h2&gt;
&lt;h3&gt;
  
  
  How do we clone?
&lt;/h3&gt;

&lt;p&gt;We initially thought we can clone with simple git clone command but sadly all we end up is with empty folders like earlier as see in screen shot.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9t3i5q4kchsdwj05yp5.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9t3i5q4kchsdwj05yp5.PNG" alt="git clone without recursive"&gt;&lt;/a&gt;&lt;br&gt;
On searching further we came to know of –recursive option which can help us in copying also the submodules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Git clone &amp;lt;url&amp;gt; --recursive. 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1eue637ps1h8vyferxl.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1eue637ps1h8vyferxl.PNG" alt="git clone with recursive"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How do we check status of all projects at one go from parent folder?
&lt;/h3&gt;

&lt;p&gt;We were thinking in case we need to check status of each project, we will need to go under each folder which is cumbersome. So does submodule help in this and it does all you need to do is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Git submodule foreach git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqs4cgr8ssubl2wzqiin7.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqs4cgr8ssubl2wzqiin7.PNG" alt="Status"&gt;&lt;/a&gt;&lt;br&gt;
If we can check status can we pull also? Yes we can using below command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Git submodule foreach git pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F623gqhh7e3uwhck6ffg4.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F623gqhh7e3uwhck6ffg4.PNG" alt="Pull"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I hope you have learnt something new from this. Would love to hear your set of learnings while using git.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>submodules</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Let&amp;#39;s build Node.js based CLI - Track real time COVID-19 vaccination slots in India </title>
      <dc:creator>Nabheet Madan</dc:creator>
      <pubDate>Sat, 15 May 2021 02:07:13 +0000</pubDate>
      <link>https://dev.to/nabheet/let-39-s-build-node-js-based-cli-track-real-time-covid-19-vaccination-slots-in-india-3jkp</link>
      <guid>https://dev.to/nabheet/let-39-s-build-node-js-based-cli-track-real-time-covid-19-vaccination-slots-in-india-3jkp</guid>
      <description>&lt;h2&gt;
  
  
  Why Build?
&lt;/h2&gt;

&lt;p&gt;As we already know the whole world is suffering from COVID-19 and the vaccination's are going in full swing everywhere. Finding a slot is getting tougher in our country India as we have huge population to be vaccinated. Numerous times we have to go to &lt;a href="https://www.cowin.gov.in/home" rel="noopener noreferrer"&gt;CoWin site&lt;/a&gt; to search for a slot and slots are always full. It is pretty time consuming and irritating. Being a developer, I thought most of the time is usually spent by us in the terminal so why can't we have a basic terminal-based app to save time. So this post will help you in two ways&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Learn how to create Node.js based CLI's&lt;/li&gt;
&lt;li&gt;Get real time info about vaccination slots for your area.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In case you are someone who like to watch then read you can watch the same&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/crrW_ltAZKo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's begin our initial set up!
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Pre-requisite&lt;/em&gt; – We are assuming you have installed Node.js and npm,If not you can install from &lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So as a first step lets initialize our project using command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter the basic details as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9nx87sou5fuwtmc2dde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9nx87sou5fuwtmc2dde.png" alt="Set Up Package.json"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will create package.json file in the folder cowinCLI. Next step is to create a bin folder which will have our index.js file containing our application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wlcftob8e93o5fdvni0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wlcftob8e93o5fdvni0.png" alt="bin folder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the index.js file and add the below mentioned first line. This actually tells the interpreter that whatever code runs below this will be handled by the node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#! /usr/bin/env node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you remember while creating we have mentioned our entry point as index.js but actually this file now exist in bin folder. So we will correct that as well as we will add one more entry. The new entry which we will add is for the keyword we want to use to call our CLI. We want to use something like cowin. So we will add this entry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"bin": {
    "cowin": "./bin/index.js"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So your package.json will look something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "vaccli",
  "version": "1.0.0",
  "description": "CLI vaccination slots",
  "main": "bin/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" &amp;amp;&amp;amp; exit 1"
  },
  "author": "Nabheet",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.21.1",
    "chalk": "^4.1.1",
    "commander": "^7.2.0",
    "inquirer": "^8.0.0",
    "node-notifier": "^9.0.1",
    "tty-table": "^4.1.3"
  },
  "bin": {
    "cowin": "./bin/index.js"
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the basic structure is set. Now before we start adding the functionality we have not given a thought to how we will fetch the data? Lets first check that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do we have any api to fetch covid vaccination slots data?
&lt;/h2&gt;

&lt;p&gt;Thank God on looking at &lt;a href="https://www.cowin.gov.in/home" rel="noopener noreferrer"&gt;Co-Win&lt;/a&gt; site they have provided us with &lt;a href="https://apisetu.gov.in/public/marketplace/api/cowin" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt;. 50% of work is done. Now all we need to do is, consume this data and work as per our need. Lets now think about what our CLI will do.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn48vqwaipa0u94cg3jv8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn48vqwaipa0u94cg3jv8.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What functions our CLI will perform?
&lt;/h2&gt;

&lt;p&gt;On looking at closely at the &lt;a href="https://cdn-api.co-vin.in/api/v2/appointment/sessions/public/calendarByDistrict?district_id=512&amp;amp;date=31-03-2021" rel="noopener noreferrer"&gt;calendar slots api&lt;/a&gt; for a district( In India we have Country comprises of States and Union Territories which in turn consists of districts) we can see it needs some kind of district id.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywrqb00i9yn3pbpfglu7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywrqb00i9yn3pbpfglu7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So looking at how do we get districts id we found another &lt;a href="https://cdn-api.co-vin.in/api/v2/admin/location/districts/16" rel="noopener noreferrer"&gt;api&lt;/a&gt; but that needs state id&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5tr3kp7nfxmedabvoobc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5tr3kp7nfxmedabvoobc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How do we get state id's another &lt;a href="https://cdn-api.co-vin.in/api/v2/admin/location/states" rel="noopener noreferrer"&gt;API&lt;/a&gt;😊&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqz4owa2urz94tp0qohu0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqz4owa2urz94tp0qohu0.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So our CLI shall does the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ability to get all states and id's&lt;/li&gt;
&lt;li&gt;Ability to get all district id's for a state id&lt;/li&gt;
&lt;li&gt;Ability to get slots by district id&lt;/li&gt;
&lt;li&gt;Ability to filter slots by ages as we have slots by 18-45 and 45 and above.&lt;/li&gt;
&lt;li&gt;Apart from this some beautification &lt;/li&gt;
&lt;li&gt;Desktop notification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to achieve this we will be using multiple &lt;a href="https://www.npmjs.com/" rel="noopener noreferrer"&gt;npm&lt;/a&gt; modules lets install them first using the below mentioned command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install axios chalk commander inquirer node-notifier tty-table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Packages to be installed&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Axios –for calling the different api's&lt;/li&gt;
&lt;li&gt;Chalk – for beautifying the console output&lt;/li&gt;
&lt;li&gt;Commander – giving the different options and command in CLI such as cowin states or cowin districts state id is here;&lt;/li&gt;
&lt;li&gt;Inquirer – for getting user input for entering the age filter&lt;/li&gt;
&lt;li&gt;Node-notifier – send desktop notification&lt;/li&gt;
&lt;li&gt;Tty-table – format our table output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's begin by creating separate functions for them. Create a util folder under cowinCLI project. Create files states.js, districts.js, config.js and slots.js in util folder. Config.js is for the configuration related common data such as table header formatting which will be used by all functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Common configuration data to be used by all functions.
exports.config = {
  headers: { "User-Agent": "Axios - console app" },
};
exports.options = {
  borderStyle: "solid",
  borderColor: "blue",
  headerAlign: "center",
  align: "left",
  color: "white",
  truncate: "...",
  width: "90%",
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let's first code our reusable States function in states.js
&lt;/h2&gt;

&lt;p&gt;If you look, we need to call the states &lt;a href="https://cdn-api.co-vin.in/api/v2/admin/location/states" rel="noopener noreferrer"&gt;API&lt;/a&gt; for that we will use our already installed npm package &lt;a href="https://www.npmjs.com/package/axios" rel="noopener noreferrer"&gt;axios&lt;/a&gt;. We are calling the api and once we got response we are formatting the table data by using tty-table package and writing the output to the console. So this function will return formatted output of states and its id's.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const axios = require('axios');
const table = require("tty-table");
const { config,options } = require('./config');
// function to return list of all states
module.exports = function() {
    axios
    .get("https://cdn-api.co-vin.in/api/v2/admin/location/states", config)
    .then((response) =&amp;gt; {
      // table formatter
      let header = [
        {
          value: "state_id",
          headerColor: "cyan",
          alias: "State ID",
          color: "white",
          align: "left",
          width: 40,
        },
        {
          value: "state_name",
          alias: "State",
          headerColor: "cyan",
          color: "white",
          align: "left",
          width: 40,
        },
      ];
      const out = table(header, response.data.states, options).render();
      console.table(out);
    })
    .catch((error) =&amp;gt; {
      console.log(error);
    });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let's code our second reusable Districts function in districts.js
&lt;/h2&gt;

&lt;p&gt;For this also we will use similar set up of axios and tty-table. Only thing to be noted is, in this function which we are exporting has an argument as stateid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const axios = require('axios');
const table = require("tty-table");
const { config,options } = require('./config');
// Function which take stateid as input and return all the formatted districts
module.exports = function(stateid) {
    axios
    .get(
      `https://cdn-api.co-vin.in/api/v2/admin/location/districts/${stateid}`,
      config
    )
    .then((response) =&amp;gt; {
      // Table header specific formatting
      let header = [
        {
          value: "district_id",
          headerColor: "cyan",
          alias: "District ID",
          color: "white",
          align: "left",
          width: 40,
        },
        {
          value: "district_name",
          alias: "District",
          headerColor: "cyan",
          color: "white",
          align: "left",
          width: 40,
        },
      ];
      // Output the results.
      const out = table(header, response.data.districts, options).render();
      console.table(out);
    })
    .catch((error) =&amp;gt; {
      console.log(error);
    });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let's code our third reusable slots function in slots.js
&lt;/h2&gt;

&lt;p&gt;For this also we will use similar set up of axios and tty-table. Only thing to be noted is in this function which we are exporting has an argument as districtid. In addition to it you can see we are using chalk and inquirer package. Chalk is used to format the headers above the table and inquirer is used for taking input from user when slots command is run. We have also used node-notifier which will send desktop notification as soon as it runs, just an example. You can modify this behavior to code your own custom logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const axios = require('axios');
const table = require("tty-table");
const chalk = require("chalk");
const notifier = require("node-notifier");
var inquirer = require("inquirer");
const { config,options } = require('./config');

// function to check slots.
module.exports = function(district) {
  //Input prompt for getting what age you want to check records.
    inquirer
    .prompt([
      {
        type: "list",
        name: "choice",
        message: "Which Age group?",
        choices: [
          {
            name: "View All",
            value: "",
          },
          {
            name: "45 Plus",
            value: "45",
          },
          {
            name: "18 - 45 ",
            value: "18",
          },
        ],
      },
    ])
    .then((answers) =&amp;gt; {
      const date = new Date();
      var todaysDate = `${date.getDate()}-${String(
        date.getMonth() + 1
      ).padStart(2, "0")}-${date.getFullYear()}`;
      console.log(
        chalk.underline.bgRed.bold(`Showing Slots from - ${todaysDate}`)
      );

      axios
        .get(
          `https://cdn-api.co-vin.in/api/v2/appointment/sessions/public/calendarByDistrict?district_id=${district}&amp;amp;date=${todaysDate}`,
          config
        )
        .then((response) =&amp;gt; {
          let finalData = [];
          let districtName;

          response.data.centers.forEach((item) =&amp;gt; {
            item.sessions.forEach((session) =&amp;gt; {
              districtName = item.district_name;
              // based on user age choice filter the data
              if (answers.choice == "") {

                let data = {
                  Center: item.name,
                  Address: item.address,
                  Date: session.date,
                  FreeSlots: session.available_capacity,
                  Age: session.min_age_limit,
                };
                finalData.push(data);
              } else if (
                answers.choice == "18" &amp;amp;&amp;amp;
                session.min_age_limit == "18"
              ) {

                let data = {
                  Center: item.name,
                  Address: item.address,
                  Date: session.date,
                  FreeSlots: session.available_capacity,
                  Age: session.min_age_limit,
                };
                finalData.push(data);
              } else if (
                answers.choice == "45" &amp;amp;&amp;amp;
                session.min_age_limit == "45"
              ) {

                let data = {
                  Center: item.name,
                  Address: item.address,
                  Date: session.date,
                  FreeSlots: session.available_capacity,
                  Age: session.min_age_limit,
                };
                finalData.push(data);
              }
            });
          });
          console.log(
            chalk.underline.bgGreen.bold(`District - ${districtName}`)
          );
          switch (answers.choice) {
            case "":
              console.log(chalk.underline.bgBlue.bold(`All ages`));
              break;
            case "45":
              console.log(chalk.underline.bgBlue.bold(`45+ Age`));
              break;
            case "18":
              console.log(chalk.underline.bgBlue.bold(`18-45 Age`));
              break;
            default:
              break;
          }
          // table formatting
          let header = [
            {
              value: "Center",
              headerColor: "cyan",
              color: "white",
              align: "left",
              width: 40,
            },
            {
              value: "Address",
              headerColor: "cyan",
              color: "white",
              align: "left",
              width: 40,
            },
            {
              value: "Date",
              headerColor: "cyan",
              color: "white",
              align: "left",
              width: 15,
            },
            {
              value: "FreeSlots",
              headerColor: "cyan",
              color: "white",
              align: "left",
              width: 20,
            },
            {
              value: "Age",
              headerColor: "cyan",
              color: "white",
              align: "left",
              width: 20,
            },
          ];
          const out = table(header, finalData, options).render();
          console.table(out);
          notifier.notify({
            title: "Vaccination Slots Available",
            subtitle: "Daily Maintenance",
            message: "Immediately go and check Vaccination slots!",
            wait: true,
          });
        })
        .catch((error) =&amp;gt; {
          console.log(error);
        });
    })
    .catch((error) =&amp;gt; {
      if (error.isTtyError) {
        // Prompt couldn't be rendered in the current environment
      } else {
        // Something else went wrong
      }
    });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our all basic functions are in place but what is pending is the actual CLI😊 Let's start building that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lets now build the CLI by updating index.js
&lt;/h2&gt;

&lt;p&gt;So far we have used all npm packages except commander it is the heart of our CLI. We will be using commander for making the sub commands as well as flag options. As can be seen below we have used both command and option. Commands for getting states, districts and slots and they have a callback function mentioned as our reusable functions under action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#! /usr/bin/env node


const program = require("commander");
// import all functions
const districts = require('../util/districts');
const states = require('../util/states');
const slots = require('../util/slots');



// adding different cli options,commands and passing callback functions in actions

program.option("-a, --available", "Output If slots available");
program
  .command("states")
  .description("Get all State Codes and descriptions.")
  .action(states);
program
  .command("district &amp;lt;stateid&amp;gt;")
  .description("Get all district of a State")
  .action(districts);

program
  .command("slots &amp;lt;districtid&amp;gt;")
  .description("Get slots for the district")
  .action(slots);

program.parse();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final touches
&lt;/h2&gt;

&lt;p&gt;So we have everything ready all we need to do is now run below command which will install our package globally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cowin states
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl0afumaiff6gti8ykvyo.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl0afumaiff6gti8ykvyo.PNG" alt="States list"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cowin districts 12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghr6u88imf92j43eer3y.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghr6u88imf92j43eer3y.PNG" alt="Districts"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cowin slots 187
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjantnfr66ij40gvprulp.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjantnfr66ij40gvprulp.PNG" alt="Slots"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can refer the &lt;a href="https://github.com/NabheetCloud/cowincli" rel="noopener noreferrer"&gt;source code&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>cli</category>
      <category>commandlineinterfact</category>
    </item>
    <item>
      <title>My Super Easy Live-Streaming set up!</title>
      <dc:creator>Nabheet Madan</dc:creator>
      <pubDate>Fri, 15 May 2020 17:07:43 +0000</pubDate>
      <link>https://dev.to/nabheet/my-super-easy-live-streaming-set-up-1on6</link>
      <guid>https://dev.to/nabheet/my-super-easy-live-streaming-set-up-1on6</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I don't know why i always had this desire (in native tongue Keeda) of sharing my learning with the community &amp;amp; improving/learning from the feedback. The journey for the same manifested via blog posts, Q&amp;amp;A on our &lt;a href="https://community.sap.com/"&gt;SAP Community&lt;/a&gt;. I was pretty satisfied with it but in parallel kept on thinking how this can be enhanced. I never knew about Live-Streaming till the time our mentor &lt;a href="https://www.linkedin.com/in/djadams/"&gt;DJ Adams&lt;/a&gt;  introduced this to us starting via his channel on Twitch then moving to &lt;a href="https://www.youtube.com/user/qmacro99"&gt;Youtube&lt;/a&gt;. The moment i heard of it, i knew this is direction where i wanted to explore more. But my initial reaction was its way too complex for me. But it turned out other wise😀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WfenaZJs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kzj5enxwz185z960voor.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WfenaZJs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kzj5enxwz185z960voor.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bare minimum What all we need to start?
&lt;/h2&gt;

&lt;p&gt;I realized all we need is a decent laptop with a webcam to start it, pretty simple right. Of course you need to decide on which platform you want to stream for example i wanted to do it on &lt;a href="https://www.youtube.com/channel/UCW8OSu54ONLbsdV30tB3sKQ"&gt;Youtube&lt;/a&gt;. When i saw DJ's stream he had his screen also as well his camera being projected in same view that is when i got to know about &lt;a href="https://obsproject.com/"&gt;OBS(Open Broadcaster Software)&lt;/a&gt;. OBS is what allows you to create different scenes for different times, such as when you are starting a stream what shall be shown to audience, when you are offline what shall come etc...&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HdeC1ozc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s0bv7nwlbaj4yfq03xct.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HdeC1ozc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s0bv7nwlbaj4yfq03xct.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
I was overwhelmed and created few scenes. Only thing which i did not like about OBS was each time i have to first schedule a session on Youtube and then provide the key manually to OBS. I found it bit irritating. So the search to ease out this experience started that is when we discovered &lt;a href="https://www.linkedin.com/company/streamlabshq/"&gt;Streamlabs&lt;/a&gt;. &lt;a href="https://www.linkedin.com/company/streamlabshq/"&gt;Streamlabs&lt;/a&gt; is built on top of OBS but with all enhanced features. The moment you login into Streamlabs using your &lt;a href="https://www.youtube.com/channel/UCW8OSu54ONLbsdV30tB3sKQ"&gt;Youtube&lt;/a&gt; account, you don't need to add a key each time. Plus it comes with very nice inbuilt widgets such as chats, subscriber notifications etc. which is like icing on the cake!&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8UlDsTwD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0jgodgf4hfrpc5dogme0.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8UlDsTwD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0jgodgf4hfrpc5dogme0.jpeg" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
So I had my streaming set up ready with a laptop, Streamlabs and a &lt;a href="https://www.youtube.com/channel/UCW8OSu54ONLbsdV30tB3sKQ"&gt;Youtube&lt;/a&gt; account. I already had an additional desktop which i used as an extended one. I was like it is done we are good...no...read on what happened after we got the feedback from my first session😀&lt;/p&gt;

&lt;h2&gt;
  
  
  Working on 1st Session Feedback!
&lt;/h2&gt;

&lt;p&gt;The first feedback which we got from session was good but my face was not clearly visible😀. I was like okay let me see what we can do. So looking around i found out we can use my phone also as a webcam using an app named &lt;a href="https://www.kinoni.com/"&gt;EpocCam&lt;/a&gt;. I ended up buying the paid version of the app its very good. So the set up got enhanced to as shown below.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5zgs2pX7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tpto21590aemhzzc7710.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5zgs2pX7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tpto21590aemhzzc7710.jpeg" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
Does the story end here no no.. the second session had another feedback read on..&lt;/p&gt;

&lt;h2&gt;
  
  
  Working on 2nd Session Feedback!
&lt;/h2&gt;

&lt;p&gt;The feedback from one of the person was about sound quality which can be enhanced if possible. I was like okay. Let me try to see what i can do, i ended up buying a micro phone, &lt;a href="https://www.amazon.in/Maono-AU-A04-Condenser-Microphone-Black/dp/B07JHDKXK4"&gt;Maono&lt;/a&gt;  which actually just worked like magic that too with default set up. I have not got into the details of modifying parameters, planning it soon. So as a result my current set up got enhanced to as shown below. I was like now everything is good now but my 3rd session led me to another change😀 read on!&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gPxwR-l1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yh2pp7lwcuxvbbfjkrbi.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gPxwR-l1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yh2pp7lwcuxvbbfjkrbi.jpeg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3rd session brings another change
&lt;/h2&gt;

&lt;p&gt;If you see we have an extend desktop, so normally on extended desktop my &lt;a href="https://www.linkedin.com/company/streamlabshq/"&gt;Streamlabs&lt;/a&gt; run and then we broadcast the laptop screen. Since during the stream we have multiple windows to be opened in one frame it makes sense to have &lt;a href="https://www.linkedin.com/company/streamlabshq/"&gt;Streamlabs&lt;/a&gt; running on laptop and desktop being used for screen sharing. I was like okay lets do it but then i realized the jugaad which i have used to set up phone😜 does not fit well here!. So that led me to add a a &lt;a href="https://www.amazon.in/gp/product/B008QS9J6Y/"&gt;Logitech webcam&lt;/a&gt; to the desktop. So the final set up looks something like as shown below. So when we livestream now, in the normal chit chat scene we are using phone and while doing live coding on desktop we switch to webcam.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OCWs28Fp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6jtp9gatmnppzyd4fwi8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OCWs28Fp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6jtp9gatmnppzyd4fwi8.jpeg" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
You can see the use of both the views in below mentioned screen shot!.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WKU8rfUl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5z9bbdvfi3vtoavya079.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WKU8rfUl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5z9bbdvfi3vtoavya079.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The next question is does it stop here?
&lt;/h2&gt;

&lt;p&gt;Obviously not, looks like the more we stream the more we learn:). I believe this set up shall keep evolving. I am just using every setting as default one and its working perfectly fine. I for sure know that i will tinker with them the more i understand the technical jargons. I hope this experience helps you in getting started into streaming and sharing of knowledge what you have. Feel free to share your setup details or provide any feedback. My rants can be heard on &lt;a href="https://www.youtube.com/channel/UCW8OSu54ONLbsdV30tB3sKQ?view_as=subscriber"&gt;Youtube&lt;/a&gt; or on &lt;a href="https://twitter.com/nabheet"&gt;Twitter&lt;/a&gt;😀&lt;/p&gt;

</description>
      <category>youtube</category>
      <category>livestreaming</category>
      <category>streamingbeginners</category>
    </item>
    <item>
      <title>My first Custom Slack App - Part 3</title>
      <dc:creator>Nabheet Madan</dc:creator>
      <pubDate>Mon, 20 May 2019 11:06:13 +0000</pubDate>
      <link>https://dev.to/nabheet/my-first-custom-slack-app-part-3-4ahh</link>
      <guid>https://dev.to/nabheet/my-first-custom-slack-app-part-3-4ahh</guid>
      <description>&lt;h1&gt;
  
  
  Blogs in the Series
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/nabheet/my-first-custom-slack-app-part-1-4o6j"&gt;Setting the backend&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/nabheet/my-first-custom-slack-app-part-2-28p5"&gt;Setting the API server to talk to Backend as well as frontend&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/nabheet/my-first-custom-slack-app-part-3-4ahh"&gt;Setting the Frontend to talk to API server &lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Deploying the app to something permanent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Research on deploying to slack app store&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  What we have done so far?
&lt;/h1&gt;

&lt;p&gt;In our previous &lt;a href="https://dev.to/nabheet/my-first-custom-slack-app-part-2-28p5"&gt;Blog&lt;/a&gt; we have set up our &lt;a href="https://nodejs.org/en/"&gt;NodeJS&lt;/a&gt; app which will talk to &lt;a href="https://www.google.com/sheets/about/"&gt;google spreadsheet&lt;/a&gt;. So lets now create our frontend slack app which will interact our NodeJS application and as well as deploy the local running NodeJS app to SAP cloud foundry trial.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZKQQqN5N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ros032he6z5az16pkuto.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZKQQqN5N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ros032he6z5az16pkuto.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Deploying the NodeJS application to SAP Cloud foundry trial
&lt;/h1&gt;

&lt;p&gt;Before actually setting app we need to deploy our app so that we can provide the callback url of the deployed application. I being from SAP background so have used the SAP Cloud Platform Cloud foundry trial version for the same, you can use any.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://cloudplatform.sap.com/index.html"&gt;SAP&lt;/a&gt; and create a free trial version.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5WNlxi6g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/tddehrjswrx8q1rqrhhs.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Once your account is set up, you need to login, choose Cloud Foundry and go with creation and activation steps 
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VOzbdyJ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/u64uokjqjud5yi7uqysg.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;You must have seen the deployed NodeJS slack application in previous picture. In order deploy an application you first need to install CF CLI follow this &lt;a href="https://developers.sap.com/india/tutorials/cp-cf-download-cli.html"&gt;SAP Developers tutorial&lt;/a&gt; to install the same. Once you have logged in and set up your endpoint and all you need to execute below mentioned command in the NodeJS app folder to deploy the app.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cf push &amp;lt;app name&amp;gt; -b https://github.com/cloudfoundry/nodejs-buildpack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Once the application is deployed you will the endpoint in the dashboard as well CLI. This endpoint of the application will be used in setting up Slack app.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qPbcw6Sv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/y76rq5n36of1qmprlzlt.png" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Setting up Slack
&lt;/h1&gt;

&lt;p&gt;Now we our endpoint ready, lets set up our slack app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://api.slack.com/apps"&gt;Slack app&lt;/a&gt; and create a new app.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NH5oZCaB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/wchuqnvkqqor8eu38nd3.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Choose Features and functionality what you want to activate. Since our app has events, interactive components, slash commands so we choose them.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5-CzpobL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pqv4fuvdvg8nbnmzv6ac.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Events set up, please note we have appended &lt;strong&gt;/slack/events&lt;/strong&gt; to the URL.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vtYBVl8z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bl0n95j9nytt65ycdm0o.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Slash Commands set up, please note we have appended &lt;strong&gt;/slack/events&lt;/strong&gt; to the URL.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zj3oY85E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fh5g950inavre28pkogl.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Bot Set up
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LwkJ5Tsu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/gyqj4j9hkpi77n6zctp6.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Interactive actions set up
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BtSyHoFQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bgyuua1ezhii26uowiq5.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;If you remember in our previous blog we have used signing secret and token you can find them as shown below.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DOfbhWkU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/f6lf05tx7c3gcxx4hdws.png" alt=""&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AhSK16tt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/z6y6em5xodmiukjvn3dk.png" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Live Demo
&lt;/h1&gt;

&lt;p&gt;Here is the live working demo. This is the basic version which can be extended as per the need.&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/hqUKNliSwr8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  What is next?
&lt;/h1&gt;

&lt;p&gt;So now we have our custom app which is deployed to SAP Cloud foundry trial up and running, next step is to find permanent place for it. What better than our OpenFaaS cloud. We will deploy this application to OpenFaaS.&lt;/p&gt;

</description>
      <category>node</category>
      <category>slack</category>
      <category>googlespreadsheet</category>
      <category>javascript</category>
    </item>
    <item>
      <title>My first Custom Slack App - Part 2</title>
      <dc:creator>Nabheet Madan</dc:creator>
      <pubDate>Mon, 20 May 2019 07:22:29 +0000</pubDate>
      <link>https://dev.to/nabheet/my-first-custom-slack-app-part-2-28p5</link>
      <guid>https://dev.to/nabheet/my-first-custom-slack-app-part-2-28p5</guid>
      <description>&lt;h1&gt;
  
  
  Blogs in the Series
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/nabheet/my-first-custom-slack-app-part-1-4o6j"&gt;Setting the backend&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/nabheet/my-first-custom-slack-app-part-2-28p5"&gt;Setting the API server to talk to Backend as well as frontend&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/nabheet/my-first-custom-slack-app-part-3-4ahh"&gt;Setting the Frontend to talk to API server &lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Deploying the app to something permanent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Research on deploying to slack app store&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  What we have done so far?
&lt;/h1&gt;

&lt;p&gt;In our previous &lt;a href="https://dev.to/nabheet/my-first-custom-slack-app-part-1-4o6j"&gt;Blog&lt;/a&gt; we have set up our database &lt;a href="https://www.google.com/sheets/about/"&gt;google spreadsheet&lt;/a&gt; and enabled the API's to interact with it. So lets now build our &lt;a href="https://nodejs.org/en/"&gt;NodeJS&lt;/a&gt; application which will talk to frontend and backend. Although this part utilize some set up from the next blog which is slack set up lets hold on to that till part 3 is out.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Mdj8xr-l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hpkh6ds1d9j4qenbakil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Mdj8xr-l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hpkh6ds1d9j4qenbakil.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Setting up modules needed for our NodeJS App
&lt;/h1&gt;

&lt;p&gt;Our NodeJS application will need some package to talk to Slack as well as spreadsheet.So luckily slack provides us with its npm package ** &lt;a href="https://www.npmjs.com/package/slack-bolt"&gt;@slack/bolt&lt;/a&gt; ** and google spread sheet provides us with ** &lt;a href="https://www.npmjs.com/package/google-spreadsheet"&gt;google-spreadsheet&lt;/a&gt; **. Lets first initialze a node project and install the two packages&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init
npm i @slack/bolt
npm i google-spreadsheet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Lets do some coding
&lt;/h1&gt;

&lt;p&gt;Since now we have everything set up lets create an index.js file and use the modules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const App               = require('@slack/bolt');
const GoogleSpreadsheet = require('google-spreadsheet');
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once we have the modules  lets add our excel spread sheet id as well as credential file and slack app token/secrets(hold on to this, we will be covering in next part). The tokens can be handled in a better way for sure, we were looking for a solution which is quick:)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;creds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./cred.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spreadsheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;GoogleSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Spreadsheet&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="nx"&gt;got&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;previous&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;signingSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;secret1&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;token&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So now we have created two objects &lt;strong&gt;app&lt;/strong&gt; for interacting with slack and &lt;strong&gt;spreadsheet&lt;/strong&gt; for interacting with backend. Going forward we will be using the methods of these objects to get our work done.&lt;/p&gt;

&lt;p&gt;Slack uses a concept called as command using which you can create dynamic things for example our poll as shown below.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lv01woTN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/518hqp2jc2dz705lsnu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lv01woTN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/518hqp2jc2dz705lsnu6.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whenever this command is fired from Slack we shall be able to listen it in our API, so adding the code to handle command /ourpoll. This will return a response which will be shown as poll like above using say method as shown below. &lt;br&gt;
The say method has a proper structure in which we need to pass the values as in response and buttons which is nothing but actions. We can use &lt;a href="https://api.slack.com/block-kit"&gt;slack bot kit builder&lt;/a&gt; where can build our response and see the corresponding structure format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.command('/ourpoll', async ({
  command,
  ack,
  say
}) =&amp;gt; {
  // Acknowledge command request
  ack();
  say({
    blocks: [{
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": command.text
        }
      },
      {
        "type": "actions",
        "elements": [{
            "type": "button",
            "text": {
              "type": "plain_text",
              "text": "Yes",
              "emoji": true
            },
            "action_id": "Yes"
          },
          {
            "type": "button",
            "text": {
              "type": "plain_text",
              "text": "No",
              "emoji": true
            },
            "action_id": "No"
          }
        ]
      }
    ]
  });

});

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once we have the response given back to slack we are also calling &lt;strong&gt;spreadsheet.useServiceAccountAuth&lt;/strong&gt; to save the data in our google spreadsheet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  spreadsheet.useServiceAccountAuth(creds, function (err) {
   spreadsheet.addRow(1, {
      Question: command.text,
      Yes: ' ',
      No: ''
    }, function (err) {
      if (err) {
        console.log(err);
      }
    });
    if (err) {
      console.log(err)
    }
  });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So now we know how to handle the commands from slack but what about the two button or actions yes/no which we have added in our response. When user provide his/her decision we should be able to handle it. That is app object has provided method action for the same. &lt;br&gt;
Lets first handle action Yes. The code can be enhanced in much much better way which i intend to do soon:). If you notice we are reading the rows of excel and then updating the relevant row with the decision.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.action('Yes', ({
    body,
    ack,
    say
}) =&amp;gt; {
    ack();
    spreadsheet.useServiceAccountAuth(creds, function(err) {
        spreadsheet.getRows(1, function(err, rows) {
            var filteredRows = rows.filter(function(el) {
                return el.question == body.message.blocks[0].text.text;
            });
            var sayConcatenated, yescount;
            filteredRows.forEach(row =&amp;gt; {
                console.log('Row count is ' + row.yescount);
                if (row.yescount == "") {
                    row.yescount = 1;
                } else {
                    row.yescount = Number(row.yescount) + 1
                }
                yescount = row.yescount;
                if (row.yes == "" || row.yes == " ") {
                    row.yes = body.user.id;
                } else {
                    row.yes = row.yes + ',' + body.user.id;
                }
                if (row.yesusername == "") {
                    row.yesusername = body.user.name;
                } else {
                    row.yesusername = row.yesusername + ',' + body.user.name;
                }
                row.save();
                let users = row.yes.split(",");
                const say = users.map(user =&amp;gt; '&amp;lt;@' + user + '&amp;gt;');
                sayConcatenated = say.reduce((acc, sayone) =&amp;gt; acc + sayone);
            });
            say(`${sayConcatenated} ${yescount} geeks said Yes for ${body.message.blocks[0].text.text}`);
        });
        if (err) {
            console.log(err)
        }
    });
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we handle action No&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.action('No', ({
    body,
    ack,
    say
}) =&amp;gt; {
    ack();
    spreadsheet.useServiceAccountAuth(creds, function(err) {
        spreadsheet.getRows(1, function(err, rows) {
            console.log(body.message.blocks);
            var filteredRows = rows.filter(function(el) {
                return el.question == body.message.blocks[0].text.text;
            });
            var sayConcatenated, nocount;
            filteredRows.forEach(row =&amp;gt; {
                if (row.nocount == "") {
                    row.nocount = 1;
                } else {
                    row.nocount = Number(row.nocount) + 1
                }
                nocount = row.nocount;
                if (row.no == "" || row.no == " ") {
                    row.no = body.user.id;
                } else {
                    row.no = row.no + ',' + body.user.id;
                }
                if (row.nousername == "") {
                    row.nousername = body.user.name;
                } else {
                    row.nousername = row.nousername + ',' + body.user.name;
                }
                row.save();
                let users = row.no.split(",");
                const say = users.map(user =&amp;gt; '&amp;lt;@' + user + '&amp;gt;');
                sayConcatenated = say.reduce((acc, sayone) =&amp;gt; acc + sayone);
            });
            say(`${sayConcatenated} ${nocount} geeks said No for ${body.message.blocks[0].text.text}`);
        });
        if (err) {
            console.log(err)
        }
    });
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  What is next?
&lt;/h1&gt;

&lt;p&gt;So now we have our API which is talking to backend as well frontend ready. In the next blog we will set up the slack part so that Slack can talk to our NodeJS app which in turn can talk to the backend. Off course we will have a demo video also for the same:)&lt;/p&gt;

</description>
      <category>node</category>
      <category>slack</category>
      <category>googlespreadsheet</category>
      <category>javascript</category>
    </item>
    <item>
      <title>My first Custom Slack App - Part 1  </title>
      <dc:creator>Nabheet Madan</dc:creator>
      <pubDate>Mon, 20 May 2019 05:36:03 +0000</pubDate>
      <link>https://dev.to/nabheet/my-first-custom-slack-app-part-1-4o6j</link>
      <guid>https://dev.to/nabheet/my-first-custom-slack-app-part-1-4o6j</guid>
      <description>&lt;h1&gt;
  
  
  Blogs in the Series
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/nabheet/my-first-custom-slack-app-part-1-4o6j"&gt;Setting the backend&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/nabheet/my-first-custom-slack-app-part-2-28p5"&gt;Setting the API server to talk to Backend as well as frontend&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/nabheet/my-first-custom-slack-app-part-3-4ahh"&gt;Setting the Frontend to talk to API server &lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Deploying the app to something permanent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Research on deploying to slack app store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;This is my first blog post here, but i have been silently learning a lot from this great community. So finally i thought its is the time to give back to the community which has given us so much. Off late i have been using &lt;a href="https://slack.com/intl/en-in/"&gt;slack&lt;/a&gt; to work closely. Recently there was a need to get some group members opinion on some questions came up. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fGKxSMKL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qxec8n0nqicsjmxmcieu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fGKxSMKL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qxec8n0nqicsjmxmcieu.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
As always i tried with few of the already existing slack applications such as &lt;a href="https://technologylearners.slack.com/apps/A0HFW7MR6-simple-poll"&gt;simple poll&lt;/a&gt; but sadly none of it is free as can be seen below.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jHR7PGWj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rqi2r8rpc1kkkg0mijqy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jHR7PGWj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rqi2r8rpc1kkkg0mijqy.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So i thought of we are the developers why can't we make a custom free poll &lt;a href="https://api.slack.com/slack-apps"&gt;app&lt;/a&gt;. This will have two benefits one  So this and next set of blogs will be about sharing my experience while doing the same, with a hope it might help someone like me. I will share the code also so that anyone can adapt as per his/her own needs. So lets begin:)&lt;/p&gt;

&lt;h1&gt;
  
  
  How we are planning to built?
&lt;/h1&gt;

&lt;p&gt;Since we want our app to be mostly running free, I thought of doing as below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A backend using &lt;a href="https://www.google.com/sheets/about/"&gt;google spread sheet&lt;/a&gt; to store the results.&lt;/li&gt;
&lt;li&gt;Our custom &lt;a href="https://nodejs.org/en/"&gt;NodeJS&lt;/a&gt; app hosted somewhere free which talks to both &lt;a href="https://www.google.com/sheets/about/"&gt;google spread sheet&lt;/a&gt; as well the &lt;a href="https://slack.com/intl/en-in/"&gt;slack&lt;/a&gt; app.&lt;/li&gt;
&lt;li&gt;Custom &lt;a href="https://slack.com/intl/en-in/"&gt;slack&lt;/a&gt; app to talk to our &lt;a href="https://nodejs.org/en/"&gt;NodeJS&lt;/a&gt;  api.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zqH4v__Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/to7rxcovn0wcukcayoks.png" alt=""&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Setting up the backend
&lt;/h1&gt;

&lt;p&gt;So in this first part we will be setting up our Google Spreadsheet set up and API's which can be consumed by the &lt;a href="https://nodejs.org/en/"&gt;NodeJS&lt;/a&gt; app in upcoming blogs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/"&gt;Google cloud platform&lt;/a&gt; provides us with free 300$ credit which are sufficient enough to run things for a year. So lets keep the ball rolling and get our &lt;a href="https://www.google.com/sheets/about/"&gt;google spread sheet&lt;/a&gt; backend up and running&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create your free &lt;a href="https://cloud.google.com/free/"&gt;GCP&lt;/a&gt; account
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GhQN1JbJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/x4s0nvhbgw31us0pjxqk.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Login to the &lt;a href="https://console.cloud.google.com"&gt;console&lt;/a&gt;-&amp;gt; Choose API and Services-&amp;gt; Dashboard -&amp;gt; Click on Enable API &amp;amp; Services-&amp;gt; Search for Google Spreadsheet -&amp;gt; Enable the Google Spreadsheet API
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NN1hr0Jh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/twca74233tlljqz4u8iu.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Save the private key file which is generated after you have enabled the API. This will be used by our NodeJS app to talk to the the backend.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c19MIUUp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uea8iff8sizt1e0t12nv.png" alt=""&gt;
&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://docs.google.com"&gt;spreadsheet&lt;/a&gt; with columns as you need.Note down the spreadsheet id which you will need to refer in NodeJS 
backend in coming blogs.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2ewYBAYY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0gl9ernigwvdwxtctb42.png" alt=""&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  What is next?
&lt;/h1&gt;

&lt;p&gt;So now our backend is ready, the next step is to get our NodeJS app i[ and running which will be interacting with both frontend as well backend. In next blog we will be discussing about the same. Please feel free to provide your feedback.&lt;/p&gt;

</description>
      <category>node</category>
      <category>slack</category>
      <category>googlespreadsheet</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
