Create dynamic custom shields for your GitHub Readme!
Tags: github, javascript, discord, python
Published: 2021-12-05 11:05:31

This article explains how to create a custom dynamic shield that will change over time using shields.io.

Ever wanted to display your discord bot servers count on your GitHub repository?
The article covers that too.

I implemented this for my discord bot, Valorina.
{% github sanjaybaskaran01/Valorina %}

<!-- TABLE OF CONTENTS -->
## Table of Contents

- [Prerequisites](#prerequisites)
- [What we are going to accomplish](#what-we-are-going-to-accomplish)
- [File Structure](#file-structure)
- [How we are going to accomplish it](#how-we-are-going-to-accomplish-it)
    - [MongoDB and Bot.py](#mongodb-and-bot.py)
    - [Creating the Backend API endpoint to generate a badge](#creating-the-backend-api-endpoint-to-generate-a-badge)
    - [Customise the shield](#customise-the-shield)
    - [Hosting the backend using vercel](#hosting-the-backend-using-vercel)

## <b> Prerequisites </b>
1. MongoDB 
2. Discord.py or Discord.js
3. Free Vercel account or any server to host backend endpoint

## <b> What we are going to accomplish </b>
Build a Github badge that display the server count of the bot and dynamically update when added to a new server

![valorina-badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fserver-count-sanjaybaskaran01.vercel.app%2Fapi)

## <b> File structure </b>

```bash
.
├── server_count (Backend folder)
│   ├── index.js
│   ├── models
│   │   └── server.js
│   ├── package.json
│   ├── package-lock.json
│   └── vercel.json
└── Valorina (Bot folder)
    ├── bot.py
    ├── db.py
```
## <b> How we are going to accomplish it </b>

### MongoDB and Bot.py

Create a new collection in mongoDB

![new collection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/effs1axhfw7jbaydqxp7.png)
 

Create a new document in your mongoDB collection

![document creation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5povxbiwc0ji9yl6917.png)
 
Copy the ID of the document which is 619637ac508090df22a468b5 for me

Now on your db.py, we are creating a function which will find this document,create a field in it called "server_count" and set the count

```python
# db.py
from pymongo import MongoClient

MONGO = "mongodb+srv://name:password@etcetc.mongodb.net" # Mongo string 
ID = "619637ac508090df22a468b5" # ID of Mongo document that will contain the server count
cluster = MongoClient(MONGO)
db = cluster["discord"] # Name of your cluster

def updateServerCount(count):
    collection = db['servers'] # Name of the collection in your cluster
    collection.update_one(
            {'_id':ObjectId(ID)},
            {"$set":{'server_count':count}})
```

Now on your bot.py, we will use updateServerCount function whenever the bot joins a new server

```python
# bot.py

import discord
import db
# All other imports

# Create a bot event or client event depending on what you are using
 
@bot.event
async def on_guild_join(guilds):
    count = len(bot.guilds) # array with all the servers but we need just its length
    db.updateServerCount(count) # function that will update server count

```
### <b> Creating the Backend API endpoint to generate a badge </b>
```bash 
npm init -y
npm i express
npm i mongoose
npm i -g vercel
```
Create a new folder called model for the schema of the document
```javascript
// /models/server.js

const mongoose = require('mongoose');

const { Schema } = mongoose;

const ServerSchema = new Schema({
    server_count: {
        type: Number,
        required: [true, 'server count is required']
    }
})

const Servers = mongoose.model('servers', ServerSchema) // servers is the name of the collection in mongoDB

module.exports = Servers;

```
Create an index.js file in the folder
```javascript
// index.js

const express = require('express');
const app = express();


const mongoose = require('mongoose')

const MONGO = "mongodb+srv://name:password@etcetc.mongodb.net" 
// Mongo string 
const ID = "619637ac508090df22a468b5" 
// ID of Mongo document that will contain the server count

mongoose.connect(MONGO, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
});

mongoose.Promise = global.Promise;

const Servers = require('./models/server');

const port = process.env.PORT || 3001


app.get('/api', async (req, res) => {
    const servers_count = await Servers.findById(ID)
    res.setHeader('Content-Type', 'application/json')
    res.json({ schemaVersion: 1, label: "Valorina #6711", message: `Server count: ${servers_count['server_count']}`, namedLogo: "Discord", labelColor: "586AEA", style: "for-the-badge", logoColor: "white", color: "FE424F" });
})

app.listen(port, () => {
    console.log(`listening to port ${port}`);
})

```

Now let us look at what we have created 

```
node index.js
listening to port 3001
```
If everything went as instructed we should receive this when we open [localhost:3001/api](http://localhost:3001/api)

![localhost](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a88cnznl9iosb0ostonj.png)
 
We have successfully built an endpoint which sheilds.io can utilise

### <b> Customise the shield </b>
Please feel free to customise the shield to your liking

![shields.io](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/acs81yqbuf89enbq9swu.png)

Once done we can head over to [shields.io/endpoint](https://shields.io/endpoint) and try previewing the shield. 

But we stumble into a problem here. shields.io does not accept endpoints from localhosts we must host this backend that we have created somewhere.

Vercel comes handy in this scene.

### <b> Hosting the backend using vercel </b>

Please feel free to host the backend in any cloud services, we are using Vercel since it is free.

run the command `vercel` in root of backend

```bash
? Set up and deploy “~/Desktop/username/vercel-node-app”? [Y/n] y
? Which scope do you want to deploy to? username
? Link to existing project? [y/N] n
? What’s your project’s name? server-count
? In which directory is your code located? ./
No framework detected. Default Project Settings:
- Build Command: `npm run vercel-build` or `npm run build`
- Output Directory: `public` if it exists, or `.`
- Development Command: None
? Want to override the settings? [y/N] n
```

We need to create a vercel.json in the root of backend,
```json
{
    "version": 2,
    "builds": [
        {
            "src": "./index.js",
            "use": "@vercel/node"
        }
    ],
    "routes": [
        {
            "src": "/(.*)",
            "dest": "/"
        }
    ]
}
```
Now run the command `vercel` again and we will find fully working deployed link which has all the routes, copy that link eg: [server-count-sanjaybaskaran01.vercel.app/api](https://server-count-sanjaybaskaran01.vercel.app/api)


Head over to shields.io/endpoint 
![shields.io endpoint](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ncy5n035jo54lh79yqz.png)

Voilà! 🎊🎊

We have successfully created a Github shield for our discord bot which will dynamically display the amount of servers in which the bot is a part of.