Developing our own cowin slot notifier in 30 mins
Tags: node, covid, javascript, cli
Published: 2021-05-16 09:04:58

Since the vaccination slots have opened all over the country; getting a slot has become as difficult as booking tatkal ticket on IRCTC. There are many apps now available who notifies when slots are available; I had an idea - Can I code my own cowin slot notifier? Will it be as effective as other apps? Turns out, the answer is yes. Without further waiting, let's jump into the code.

We will develop a very minimalistic NodeJs application which will notify us when the slots are available.

## Pre-Requisites
1. Cowin Public APIs - https://apisetu.gov.in/public/api/cowin
2. Some knowledge of a NodeJs.

## Part 1: Fetching the data
The API which we need to target are Appointment Availability APIs. Following APIs are exposed by Cowin:
![Appointment Availability APIs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4epjn69q4yhxty3u4aqd.png)
We will use `/v2/appointment/sessions/public/findByDistrict` but one can use other API as well and the logic will be same. This API takes in 2 query parameters:
1. **district_id** - To get the district_id, we need to hit the meta APIs from the swagger. Open the swagger [link](https://apisetu.gov.in/public/api/cowin) and hit get states API under Meta APIs - */v2/admin/location/states*. Pick the state_id for your state. Then using this state_id hit, get list of districts under Meta APIs - */v2/admin/location/districts/{state_id}*. Pick the `district_id` for your state. We will hardcode this value for out main API. For sample reference, we will use state as *Delhi* and district as *South Delhi*. The relevant `state_id is 9` and `district_id is 149`.
2. **date**   - The date in `DD-MM-YYYY` format for which we want slots.   

We won't send any header value (I think the APIs are not consuming this header properly and giving incorrect response if you use them).

Now we have all data to get the slots. Using the https node module we will make a GET call to `https://cdn-api.co-vin.in/api/v2/appointment/sessions/public/calendarByDistrict?district_id={district_id}&date={date}`

Sample Code for the same is:
```javascript
https.get('https://cdn-api.co-vin.in/api/v2/appointment/sessions/public/calendarByDistrict?district_id=149&date=16-05-2021', (resp) => {
  let data = '';

  resp.on('data', (chunk) => {
    data += chunk;
  });

  resp.on('end', () => {
    processData(JSON.parse(data))
  });

}).on("error", (err) => {
  console.log("Error: " + err.message);
});
```
This will give us the API response in `data` variable which we will pass to another function for processing.

*Note:* We need to call the api regularly at frequent intervals so as to get the slots as soon as they are available. The Cowin doc says API have limit of 100 calls per 5 minutes = One call per 3 second. We will use setInterval function for the same and clearInterval as soon as the slots are found.

## Part 2: Processing the data
The response we have got is for all the centres of the district. Here is one sample response:
```json
{
  "centers": [
    {
      "center_id":701523,
      "name":"P. SKV School Fatehpurberi S-4",
      "address":"4 Church Wali Gali Fatehpur Beri New Delhi Delhi",
      "state_name":"Delhi",
      "district_name":"South Delhi",
      "block_name":"Not Applicable",
      "pincode":110074,
      "lat":28,
      "long":77,
      "from":"09:00:00",
      "to":"17:00:00",
      "fee_type":"Free",
      "sessions": [
        {
          "session_id":"23e393ea-a06e-42d7-860f-83ecd45edb5f",
          "date":"16-05-2021",
          "available_capacity":0,
          "min_age_limit":18,
          "vaccine":"COVISHIELD",
          "slots": [
            "09:00AM-11:00AM",
            "11:00AM-01:00PM",
            "01:00PM-03:00PM",
            "03:00PM-05:00PM"
          ],
          "available_capacity_dose1":0,
          "available_capacity_dose2":0
        }
      ]
    }
  ]
}
```

The response contains a lot of info and we will write few conditions to filter the data. Note that each object under sessions array, contains `available_capacity` key which tells the availability of the slots and `min_age_limit` key tells the age limit for which slots are open. Based on these 2 keys and other factors such as pincode, let's filter out the results
```javascript
const pincodes = [110030, 110062]
const centers = data.centers
for (let i = 0; i < centers.length; ++i) {
  if (pincodes.indexOf(centers[i].pincode) >= 0) {
    const sessions = centers[i].sessions
    for (let j = 0; j < sessions.length; ++j) {
      if (sessions[j].available_capacity > 0) {
        slotFound = true
        console.log(`Slots Found:
            Pincode: ${centers[i].pincode}
            Centre Name: ${centers[i].name}
            Centre Address: ${centers[i].address}
            Capacity Remaining: ${sessions[j].available_capacity}
        `)
        notify()
      }
    }
  }
}
```
Once we have filtered out the results, we need a mechanism to notify us. 

## Part 3: The Notification
Since, I will be running this application locally in my laptop, I thought of using my Mac's audio player `afplay` to notify me (Ubuntu equivalent for afplay is ffplay). We just need one audio file in our system and `exec` from `child_process`. Using exec, we can run bash commands in node. As soon as any slot is found, we can run the following command. It will start to play the music in our system thus alerting us (:D).
```javascript
exec('afplay ./audio.mp3')
```
Stitching everything together, here is the full code:
{% gist https://gist.github.com/sunil12738/a71063fd57356cf447462bbfc1f13855 %}

Go ahead, save this file and run it in your system.

## Next Steps
This is a working POC done in 30 mins and can be improved a lot. There are a lot of improvements that can be:
- Remove the hardcoding at various places and make the application generic for all states and districts
- Support to search for slots by different APIs
- Create UI and deploy the same on a website (and change the notification mechanism) to make it suitable for all users

Please comment for any valuable feedback, comment or criticism.

Thanks!