{"id":22897728,"url":"https://github.com/newtonmunene99/wa-chatbot","last_synced_at":"2025-08-18T21:05:27.155Z","repository":{"id":39911530,"uuid":"231549128","full_name":"newtonmunene99/wa-chatbot","owner":"newtonmunene99","description":"Creating a Whatsapp chatbot using Node JS, Dialogflow and Twilio","archived":false,"fork":false,"pushed_at":"2022-12-11T19:11:45.000Z","size":398,"stargazers_count":24,"open_issues_count":11,"forks_count":13,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-07T22:08:57.274Z","etag":null,"topics":["chatbot","dialogflow","node-js","twilio","twilio-sms-api","twilio-whatsapp-sandbox","wa-chatbot","whatsapp","whatsapp-chatbot"],"latest_commit_sha":null,"homepage":"https://blog.newtonmunene.me/creating-a-whatsapp-chatbot-using-node-js-dialogflow-and-twilio-ck4xxulw3004vmus1a2gr9su1","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/newtonmunene99.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-01-03T08:53:36.000Z","updated_at":"2025-04-09T19:14:36.000Z","dependencies_parsed_at":"2023-01-27T05:16:09.322Z","dependency_job_id":null,"html_url":"https://github.com/newtonmunene99/wa-chatbot","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/newtonmunene99%2Fwa-chatbot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/newtonmunene99%2Fwa-chatbot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/newtonmunene99%2Fwa-chatbot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/newtonmunene99%2Fwa-chatbot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/newtonmunene99","download_url":"https://codeload.github.com/newtonmunene99/wa-chatbot/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252961841,"owners_count":21832197,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chatbot","dialogflow","node-js","twilio","twilio-sms-api","twilio-whatsapp-sandbox","wa-chatbot","whatsapp","whatsapp-chatbot"],"created_at":"2024-12-14T00:18:55.793Z","updated_at":"2025-05-07T22:09:12.883Z","avatar_url":"https://github.com/newtonmunene99.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wa-chatbot\n\nCreating a Whatsapp chatbot using Node JS, Dialogflow and Twilio.\n\n**Read the original post [on my blog](https://blog.newtonmunene.me/creating-a-whatsapp-chatbot-using-node-js-dialogflow-and-twilio-ck4xxulw3004vmus1a2gr9su1)**\n\nI'm going to walk you through creating a WhatsApp chatbot using Dialogflow. I initially wanted to do this in several blog posts but I'm going to cover it all in one. Brace yourself it might be a bit long.\n\n# Prerequisites\n\n1.  [Twilio](www.twilio.com/referral/KqKLx8)  account\n2. Dialogflow account\n3. Node Js\n4. Javascript knowledge\n\n# Getting Started\n\nInstall the latest stable version of Node Js if you don't have it. \n\n# Twilio\nVisit  [twilio](www.twilio.com/referral/KqKLx8) and sign up for a new account if you do not have one. You'll be given some credits to get you started. After they are over you'll need to pay to get more so use them wisely.\n\nOn your dashboard take note of your `Account SID` and `Auth Token`.\nHead over to [https://www.twilio.com/console/sms/whatsapp/learn](https://www.twilio.com/console/sms/whatsapp/learn) and follow the instructions to connect your Whatsapp account to your sandbox. This is necessary for the sandbox environment. This is all you need for now, we'll come back to this later.\n\n# Dialogflow\nAccording to their  [website](https://cloud.google.com/dialogflow/) , \n\u003e Dialogflow is an end-to-end, build-once deploy-everywhere development suite for creating conversational interfaces for websites, mobile applications, popular messaging platforms, and IoT devices. You can use it to build interfaces (such as chatbots and conversational IVR) that enable natural and rich interactions between your users and your business. Dialogflow Enterprise Edition users have access to Google Cloud Support and a service level agreement (SLA) for production deployments.\n\nWe will be using Dialogflow to power our chatbot. Head over to  [Dialogflow Console](https://dialogflow.cloud.google.com/) and create a new agent. I will not dive into the specifics of creating and training agents, handling entities, intents and more. That is beyond the scope of this tutorial. You can find multiple resources on this online. \n\nAfter creating your agent, click on the Small Talk tab on the left and enable it. This allows our bot to respond to small talk and common phrases. You can customize the responses on the same tab. You can do this for a more personalized experience. On the right side of your Dialogflow console, there's an input field where you can test out your bot.  \n\nWhen you've tested out your bot and are satisfied you can now follow the steps below to set up authentication for accessing your chatbot via an API. \n\n1. On your Dialogflow console, open settings by clicking on the gear icon next to our project name.\n2. Take note of the Project Id that is on the General tab of the settings page under Google Project section. We'll be using that later.\n3. Follow the link next to Service Account.\n\n![Screenshot from 2020-01-03 10-54-09.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1578038286605/d-43Cv2JA.png)\n- Create a new service account, give it an appropriate name.\n\n![Screenshot from 2020-01-03 11-00-06.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1578038475110/MHbbdz5dm.png)\n- Set Dialogflow role to Dialogflow API Admin\n\n![Screenshot from 2020-01-03 11-00-26.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1578038514884/R1t7VCjYK.png)\n- Create a new key and choose JSON. \n\n![Screenshot from 2020-01-03 11-00-52.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1578038576712/FStn7eegM.png)\n- Rename the downloaded JSON file to `credentials.json`. This is just so we can reference it easily. We will come back to this file later.\n\n\n\n# Backend (Node JS)\n\nI will be using typescript in this project. You shouldn't feel intimidated even if you haven't used it before. You can check out  [this guide](https://levelup.gitconnected.com/setup-express-with-typescript-in-3-easy-steps-484772062e01)  on how to get started with typescript and express.\n\nOpen your terminal and create a new project.\n\n```bash\nmkdir wa-chatbot \u0026\u0026 cd wa-chatbot\n```\n\nInitialize a new node js project inside the folder we just created and changed directory into\n\n```bash\nnpm init -y\n```\n\nInstall the following dev dependencies\n1. nodemon\n2. typescript\n3. ts-node\n4. ts-lint\n\n```bash\nnpm i -D nodemon typescript ts-node ts-lint\n```\n\nInstall the following dependencies\n1. express\n2. dotenv\n3. twilio\n4. dialogflow\n5. @overnightjs/core\n6. @overnightjs/logger\n7. body-parser\n8. cors\n\nWe're using Overnight to stay closer to the MVC pattern and utilize Object Oriented style of programming. Read more about Overnight  [here](https://github.com/seanpmaxwell/overnight) . \n\n```bash\nnpm i -S express dotenv twilio dialogflow @overnightjs/core @overnightjs/logger body-parser cors\n```\n\nWe also need to install types for these modules\n\n```bash\nnpm i -D @types/node @types/express @types/twilio @types/dialogflow @types/body-parser @types/cors\n```\n\nNext, we'll create a `tsconfig.json` file. In the root of your project make a new file.\n\n```bash\ntouch tsconfig.json\n```\n\nCopy and paste the following content inside the new file\n\n```json\n{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"strict\": true,\n    \"baseUrl\": \"./\",\n    \"outDir\": \"build\",\n    \"removeComments\": true,\n    \"experimentalDecorators\": true,\n    \"target\": \"es6\",\n    \"emitDecoratorMetadata\": true,\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"types\": [\"node\"],\n    \"typeRoots\": [\"node_modules/@types\"]\n  },\n  \"include\": [\"./src/**/*.ts\"],\n  \"exclude\": [\"./src/public/\"]\n}\n```\nI will not go into the specifics for now but you can read typescript documentation for more information.\nNext, create a `tslint.json` file at the root of your project and paste the following content inside.\n\n```json\n{\n  \"defaultSeverity\": \"warning\",\n  \"extends\": [\"tslint:recommended\"],\n  \"jsRules\": {},\n  \"rules\": {\n    \"trailing-comma\": [false],\n    \"no-bitwise\": false,\n    \"jsdoc-format\": true,\n    \"deprecation\": true,\n    \"interface-name\": true,\n    \"no-duplicate-imports\": true,\n    \"no-redundant-jsdoc\": true,\n    \"no-use-before-declare\": true,\n    \"variable-name\": false,\n    \"object-literal-sort-keys\": false,\n    \"member-ordering\": true,\n    \"await-promise\": true,\n    \"curly\": true,\n    \"no-async-without-await\": true,\n    \"no-duplicate-variable\": true,\n    \"no-invalid-template-strings\": true,\n    \"no-misused-new\": true,\n    \"no-invalid-this\": true,\n    \"prefer-const\": true\n  },\n  \"rulesDirectory\": []\n}\n```\n\nLet's set up our backend structure.\nOpen your terminal and run the following commands inside the root of your project.\n\n```bash\nmkdir src \u0026\u0026 touch src/AppServer.ts \u0026\u0026 touch src/start.ts\n```\n\n`AppServer.ts` is where we will set up our express app.\nPaste the following inside `src/AppServer.ts`\n\n```typescript\nimport * as bodyParser from \"body-parser\";\nimport * as controllers from \"./controllers\";\nimport { Server } from \"@overnightjs/core\";\nimport { Logger } from \"@overnightjs/logger\";\nimport * as cors from \"cors\";\nexport class AppServer extends Server {\n  private readonly SERVER_STARTED = \"Server started on port: \";\n\n  constructor() {\n    super(true);\n    this.app.use(bodyParser.json());\n    this.app.use(bodyParser.urlencoded({ extended: true }));\n    this.app.use(cors());\n    this.setupControllers();\n  }\n\n  private setupControllers(): void {\n    const ctlrInstances = [];\n    for (const name in controllers) {\n      if (controllers.hasOwnProperty(name)) {\n        const controller = (controllers as any)[name];\n        ctlrInstances.push(new controller());\n      }\n    }\n    super.addControllers(ctlrInstances);\n  }\n\n  public start(port: number): void {\n    this.app.get(\"*\", (req, res) =\u003e {\n      res.send(this.SERVER_STARTED + port);\n    });\n    this.app.listen(port, () =\u003e {\n      Logger.Imp(this.SERVER_STARTED + port);\n    });\n  }\n}\n\n```\nIn this file, we set up our AppServer class which extends the Server class from Overnight. In the constructor we initialise the Server class passing true as a parameter. We then head on to configure some middlewares for our app. We might be receiving JSON data in our requests so we use `body-parser` to ensure it's handled properly.\n\nWe then set up our controllers which we will create in a short while. After this, we define the `start` method which will start up the app on the port passed to it as a parameter\n\nPaste this into `src/start.ts`. This is the starting point for our application.\n\n```typescript\nimport { config } from \"dotenv\";\nconfig();\nimport { AppServer } from \"./AppServer\";\n\nconst appServer = new AppServer();\nappServer.start(3000);\n```\n\nAt the top, we import `config` from `dotenv` and call it. We use this to configure our environment variables and load them into `process.env`. We also initiate a new instance of the server and call the `start` method passing in a port to use. \n\nAt this point, we need to set up our controllers. Create a folder inside `src` and call it `controllers`. Inside `src/controllers` create two files: `BotController.ts` and `index.ts`\n\nInside `BotController.ts` paste the following code\n\n```typescript\nimport { Request, Response } from \"express\";\nimport { Controller, Post } from \"@overnightjs/core\";\nimport { Logger } from \"@overnightjs/logger\";\n\n@Controller(\"api/bot\")\nexport class BotController {\n  \n}\n```\nYou'll notice some weird syntax just before our controller class. That's a decorator. We use it to tell the compiler that our class is a controller. We also pass an argument which is our URL path. With this, we can now make restful requests to `[SERVER]:[PORT]/api/bot`. \n\nWe don't have any routes defined yet. For Twilio, we will only need a POST route. Inside the BotController class add the following code.\n\n```typescript\n@Post()\n  private postMessage(request: Request, response: Response) {\n    Logger.Info(\"A post request has been received\");\n    return response.status(200).json({\n      message: \"A post request has been received\"\n    });\n  }\n```\nYou'll notice another decorator which tells our compiler that the method handles POST requests.\n\nIn `src/controllers/index.ts` add the following code. This exports our controllers so that it will be easy to export any future controllers.\n\n```typescript\nexport * from \"./BotController\";\n```\n\n## The fun stuff\n\nIt's time to get to the fun stuff. Let's set up our app to communicate with Twilio and Dialogflow.\nCreate a folder called `utils` under `src`. Inside utils create two files: `dialogflow.ts` and `twilio.ts`\n\nInside Dialogflow.ts:\n\n```typescript\n// dialogflow.ts\n\nconst dialogflow = require(\"dialogflow\");\nconst credentials = require(\"../../credentials.json\");\n\nconst sessionClient = new dialogflow.SessionsClient({\n  credentials: credentials\n});\nconst projectId: string = process.env.DIALOGFLOW_PROJECT_ID!;\n\nexport const runQuery = (query: string, number: string) =\u003e {\n  return new Promise(async (resolve, reject) =\u003e {\n    try {\n      // A unique identifier for the given session\n      //const sessionId = uuid.v4();\n      const sessionId = number;\n      // Create a new session\n\n      const sessionPath = sessionClient.sessionPath(projectId, sessionId);\n\n      // The text query request.\n      const request = {\n        session: sessionPath,\n        queryInput: {\n          text: {\n            // The query to send to the dialogflow agent\n            text: query,\n            // The language used by the client (en-US)\n            languageCode: \"en-US\"\n          }\n        }\n      };\n\n      // Send request and log result\n      const responses = await sessionClient.detectIntent(request);\n\n      const result = responses[0].queryResult;\n\n      resolve(result);\n    } catch (error) {\n      reject(error);\n    }\n  });\n};\n```\nHere we're importing Dialogflow and also the credentials.json file we downloaded when setting up our chatbot on Dialogflow. Remember that file? Move it to your project's root folder. We're the setting up a new SessionsClient using the credentials file. In our `runQuery` function we're taking in a query to send to Dialogflow and also the user's Whatsapp number which we will use to set up a Dialogflow session unique to that user. We then send the query to Dialogflow and return the response.\n\nIn `twilio.ts` add the following code :\n```typescript\nimport { Twilio } from \"twilio\";\nconst accountSid = process.env.TWILIO_ACCOUNT_SID!;\nconst authToken = process.env.TWILIO_AUTH_TOKEN!;\n\nconst client = new Twilio(accountSid, authToken);\n\nexport const sendMessage = (to: string, from: string, body: string) =\u003e {\n  return new Promise((resolve, reject) =\u003e {\n    client.messages\n      .create({\n        to,\n        from,\n        body\n      })\n      .then(message =\u003e {\n        resolve(message.sid);\n      })\n      .catch(error =\u003e {\n        reject(error);\n      });\n  });\n};\n```\nHere we create a new Twilio client and instantiate using our Twilio Account SID and Auth Token. We then call the `client.messages.create` function which takes in the number of the user, the number sending the message(in this case, the Twilio sandbox number) and also a body. We then return the message Id.\n\nYou've probably noticed we've used a few environment variables that we haven't defined yet. In the root of your project create a `.env` file. Inside paste the following code and make sure to replace placeholders with appropriate values. I asked you to take note of the values required at some point in this tutorial.\n\n```\nTWILIO_ACCOUNT_SID=PLACEHOLDER\nTWILIO_AUTH_TOKEN=PLACEHOLDER\nDIALOGFLOW_PROJECT_ID=PLACEHOLDER\n```\n\nGo back to `BotController.ts` and replace the `postMessage` method with the following code.\n\n```typescript\n@Post()\n  private postMessage(request: Request, response: Response) {\n    // Here we get the message body, the number to which we're sending the message, and the number sending the message.\n    const { Body, To, From } = request.body;\n\n    // Here we're sending the received message to Dialogflow so that it can be identified against an Intent.\n    runQuery(Body, From)\n      .then((result: any) =\u003e {\n        // We send the fulfilment text received back to our user via Twilio\n        sendMessage(From, To, result.fulfillmentText)\n          .then(res =\u003e {\n            console.log(res);\n          })\n          .catch(error =\u003e {\n            console.error(\"error is \", error);\n            Logger.Err(error);\n          });\n      })\n      .catch(error =\u003e {\n        console.error(\"error is \", error);\n        Logger.Err(error);\n      });\n    return response.status(200).send(\"SUCCESS\");\n  }\n```\n\nTwilio hits this method when it receives a message from Whatsapp. We extract the message body, the sender, and the recipient(in this case, Twilio sandbox number). We then send the received body to Dialogflow and get a fulfilment text. We use the Twilio client we set up earlier to send back the fulfilment text to the user.\n\nNow there's only one more thing left to do. Open up your `package.json` and replace the scripts with the following\n\n```json\n\"scripts\": {\n    \"tsc\": \"tsc\",\n    \"prestart\": \"npm run build\",\n    \"dev\": \"ts-node src/start.ts\",\n    \"dev:watch\": \"nodemon\",\n    \"build\": \"rm -rf ./build/ \u0026\u0026 tsc\",\n    \"start\": \"node build/start.js\"\n  },\n```\nThe full file looks this \n```json\n{\n  \"name\": \"wa-chatbot\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"build/start\",\n  \"scripts\": {\n    \"tsc\": \"tsc\",\n    \"prestart\": \"npm run build\",\n    \"dev\": \"ts-node src/start.ts\",\n    \"dev:watch\": \"nodemon\",\n    \"build\": \"rm -rf ./build/ \u0026\u0026 tsc\",\n    \"start\": \"node build/start.js\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"@types/body-parser\": \"^1.17.1\",\n    \"@types/cors\": \"^2.8.6\",\n    \"@types/dialogflow\": \"^4.0.4\",\n    \"@types/express\": \"^4.17.2\",\n    \"@types/node\": \"^13.1.2\",\n    \"@types/twilio\": \"^2.11.0\",\n    \"nodemon\": \"^2.0.2\",\n    \"ts-lint\": \"^4.5.1\",\n    \"ts-node\": \"^8.5.4\",\n    \"typescript\": \"^3.7.4\"\n  },\n  \"dependencies\": {\n    \"@overnightjs/core\": \"^1.6.11\",\n    \"@overnightjs/logger\": \"^1.1.9\",\n    \"body-parser\": \"^1.19.0\",\n    \"cors\": \"^2.8.5\",\n    \"dialogflow\": \"^1.0.0\",\n    \"dotenv\": \"^8.2.0\",\n    \"express\": \"^4.17.1\",\n    \"twilio\": \"^3.39.1\"\n  }\n}\n```\nYou can run `npm install` again in case I missed any dependencies. We also need to set up nodemon. Create a `nodemon.json` in your project's root folder and paste the following inside.\n\n```json\n{\n  \"watch\": [\"src\"],\n  \"ext\": \"ts\",\n  \"ignore\": [\"src/public\"],\n  \"exec\": \"NODE_ENV=development ts-node src/start.ts\"\n}\n```\n\nYou can now run `npm run dev:watch` and see if your project runs successfully. Next, we need to expose our local server.  [Ngrok](https://ngrok.com/)  is one of the best open-source solutions but you can use whatever you prefer. \n\nCopy your exposed server URL and open  [Twilio Whatsapp Sandbox](https://www.twilio.com/console/sms/whatsapp/sandbox). Replace the URL in **WHEN A MESSAGE COMES IN** with your exposed URL. Don't forget to add the path to our bot controller. i.e. `/api/bot`\n\n![Screenshot from 2020-01-03 11-42-28.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1578040961273/FQIcqbgcE.png)\nSave the new changes and you can now send a message to the Twilio Sandbox Number and you'll see the response. \n\n\n![Screenshot_20200103-113203_WhatsApp.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1578041040285/slhCjt7wV.jpeg)\n\nHere's some homework for you. Create a new controller to handle POST requests and pass the URL to the **STATUS CALLBACK URL** input on the [Twilio Whatsapp Sandbox](https://www.twilio.com/console/sms/whatsapp/sandbox). Be creative and be notified when a user reads your message and when it's delivered.\n\nThe full source code can be found here  [https://github.com/newtonmunene99/wa-chatbot](https://github.com/newtonmunene99/wa-chatbot)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnewtonmunene99%2Fwa-chatbot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnewtonmunene99%2Fwa-chatbot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnewtonmunene99%2Fwa-chatbot/lists"}