DEV Community

Emmanuel Aiyenigba
Emmanuel Aiyenigba

Posted on • Updated on

Tutorial: create a WhatsApp bot using Node.js and Puppeteer

Bot

Hi there! Today you are going to be learning how to create a WhatsApp chat messenger with Node.js and Puppeteer. Awesome stuff, right? Yeah!

Introduction

So recently, I decided to spam my girlfriend with 200 unsolicited "I love you" WhatsApp messages. I know spamming someone is bad, forgive me!
Yeah, you can guess her reaction. Initially, it was sweet (that's because I wrote a delay in the code) until it became persistent and almost never ending (but hey! It was just 500 messages or should I say spams 😉) then she yelled in a chat "stop spamming my phone"

I am not about to teach you to spam people. This tutorial is aimed at teaching you how to use Puppeteer to create a bot - a WhatsApp messenger bot in this case.

Now let's get right into it.

Requirement

  • You should have Node installed locally on your computer.
  • Have a working knowledge of JavaScript

What is Puppeteer

Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

Note: When you install Puppeteer, it downloads a recent version of Chromium (~170MB Mac, ~282MB Linux, ~280MB Win) that is guaranteed to work with the API. You can actually skip the download if you want to. Revert to the Puppeteer docs to see how to do it

Let's proceed

Let's create an Express app to begin with

npx express-generator whatsapp-bot
Enter fullscreen mode Exit fullscreen mode

After which you should run npm install to install the necessary dependencies.

Also, let's install Puppeteer because this is like the main thing we need in this tutorial.

npm install puppeteer
Enter fullscreen mode Exit fullscreen mode

Now that puppeteer is installed, let's get our hands dirty writing some codes.

ooooooooh!

Let's create a file called bot.js inside the route directory. This is where we are going to be writing all our wonderful codes. yeeeeeah!

Remember to configure the route for bot.js inside the app.js file. I'm sure you know how to do that.

No worries if you don't just look at the below codes

const bot = require("./routes/bot");

//now add bot to the list of middlewares

app.use("/bot", bot)

Enter fullscreen mode Exit fullscreen mode

Okay, I know you are wondering why we wrote this above lines of code. Why didn't we just build our stuff inside app.js? why did we even give it a /bot?

Hey calm down! Okay? I'm on your side here. I'm sorry I put you into the stress of doing that. I did that so you can have a well organized file structure incase the tutorial gets interesting and you want to build something big.

Now that I have been able to make you see that I'm actually your friend, let's continue.

oh yeaaah!

Navigate into bot.js and let's begin


const express = require("express")
const puppeteer = require("puppeteer")

const bot = express.Router()

bot.get("/", (req, res) =>{


})

module.exports = bot;

Enter fullscreen mode Exit fullscreen mode

Just some basic import and export thingy above. Let's continue.

const express = require("express")
const puppeteer = require("puppeteer")

const bot = express.Router()

bot.get("/", async (req, res) =>{

 try{
   const browser = await puppeteer.launch({ headless: false });
   const page = await browser.newPage();

 }  

})

module.exports = bot;

Enter fullscreen mode Exit fullscreen mode

The above line of code is to help us launch Puppeteer and open a new browser page. We set headless: false so that we can actually see what is taking place in the browser. Love that right? Yeah, I know you do.
NB: remember to do headless: true before deploying online

So now let's tell Puppeteer to open WhatsApp

const express = require("express")
const puppeteer = require("puppeteer")

const bot = express.Router()

bot.get("/", async (req, res) =>{

 try{
   const browser = await puppeteer.launch({ headless: false });
   const page = await browser.newPage();

    await page.goto("https://web.whatsapp.com/");
        await page.setDefaultTimeout(0);
        await page.waitForSelector('[data-testid="search"]')
        .then(()=>  page.click('[data-testid="search"]', {
          delay: 3000
        }))

 }  

})

module.exports = bot;

Enter fullscreen mode Exit fullscreen mode

We did a few things above. We instructed Puppeteer to go to the provided URL - WhatsApp's URL. Of course we added await because it's an asynchronous call and we don't know how long it will take to get a response.
setDefaultTimeout(0) is to ensure that Puppeteer does not snap out of the page because it's taking too much time to load or open.

Oh, before I forget. You have to scan the QR code on the WhatsApp web before you login. Don't worry Puppeteer will wait for you to do that, thanks to setDefaultTimeout(0)

What did we do next?

We waited for the search button selector to load. data-testid="search" is the selector I chose to use. Well, you could use the classname, id or just any dynamic identifier you find inside the particular html tag. Use the DevTool inspect to get this. Got it?

No?

Yes!

Oh, yes! That' great. I'm sorry I didn't hear you correctly at first. eeeeh!

Let's proceed.
After selector is loaded, .then() we instructed Puppeteer to .click on the search button (so that we can search out the person we will be sending our message to.

We added a delay(3000) to cause some little delay. We don't want to be too fast so that WhatsApp won't detected we are using a bot. Smart, right?

Let's keep going

//...
  await page.type("._13NKt", "EmmanuelTheCoder");
        delay(2000)
        await page.keyboard.press("Enter")
        delay(2000)

        let messageAmount = 5;

        for(let i = 0; i<messageAmount; i++){
          delay(2000)

          await page.type(".p3_M1", "Hi, how are you, I hope you 
          are good!");
          delay(2000)
          await page.keyboard.press("Enter");


        }


        res.send("done")
      } catch (e) {
        console.error("error mine", e);
      }
//...
Enter fullscreen mode Exit fullscreen mode

Did you just scream whooooooooooooooooooooa what the hell are these codes above?

Take a deep breathe. It's alright, it's alright. I'm going to take them away right now(whispers: I'll take them away by explaining:));

await page.type("._13NKt", "EmmanuelTheCoder") this line of code tells Puppeteer to type in EmmanuelTheCoder inside the the search box with the class name ._13NKt.

NB: replace "EmmanuelTheCoder" with the contact you wish to send a message to.

You already know what delay does.

await page.keyboard.press("Enter") after typing in the name, we press the "Enter" key.

we set messageAmount to be 5. That's because we want to send our chat 5 times to EmmanuelTheCoder (I just hope he won't be annoyed with that)

Now we loop through. Everytime i < messageAmount we type in our message inside .p3_M1 - the class name for the WhatsApp message box. No! I didn't get that by divination. I only used the browser DevTool.

And then we press enter to send

We are catch-ing just incase there is an error.

Oh yeah, I did res.send("done") to signify that the message have been sent successfully. Since it's an "I did" and not a "we did", you are free to omit that code. I was only being selfish.

There is one more thing to include. I deliberately skipped that in the code above and I'm sneaking it here. That's why you should always endeavor to read a tutorial to the end.

that is......... browser.close()

Yes, you really want to close the browser when you are done. So go back and include that in the code.

You have the complete code below. Feel free to comment if you find anything difficult.

Kindly share this!

//full code
const express = require("express")
const puppeteer = require("puppeteer")

const bot = express.Router()

bot.get("/", async (req, res) =>{

 try{
   const browser = await puppeteer.launch({ headless: false });
   const page = await browser.newPage();

    await page.goto("https://web.whatsapp.com/");
        await page.setDefaultTimeout(0);
        await page.waitForSelector('[data-testid="search"]')
        .then(()=>  page.click('[data-testid="search"]', {
          delay: 3000
        }))
       await page.type("._13NKt", "EmmanuelTheCoder");
        delay(2000)
        await page.keyboard.press("Enter")
        delay(2000)

        let messageAmount = 5;

        for(let i = 0; i<messageAmount; i++){
          delay(2000)

          await page.type(".p3_M1", "Hi, how are you, I hope you 
          are good!");
          delay(2000)
          await page.keyboard.press("Enter");
        }
        res.send("done")
      } catch (e) {
        console.error("error mine", e);
      }

 }  

})

module.exports = bot;
Enter fullscreen mode Exit fullscreen mode

Conclusion

In conclusion, building a WhatsApp bot with Puppeteer and Node.js opens up a world of possibilities for developers seeking to automate interactions and streamline processes on the popular messaging platform. The integration of Puppeteer, a powerful headless browser automation library, with the versatility of Node.js provides a robust framework for creating intelligent and responsive WhatsApp bots.

Throughout this tutorial, we've explored the essential steps, from setting up the project and installing dependencies to crafting interactions with the WhatsApp web interface. Leveraging Puppeteer's capabilities, developers can navigate, manipulate DOM elements, and send messages programmatically, enabling the creation of sophisticated bots tailored to diverse use cases.

As with any automation tool, it's crucial to adhere to ethical guidelines, privacy policies, and terms of service when deploying WhatsApp bots. Respecting user consent and ensuring compliance with platform regulations contribute to a positive and responsible development environment.

The combination of Puppeteer and Node.js empowers developers to build efficient and reliable WhatsApp bots, offering endless possibilities for automation, notification systems, and personalized user experiences. As technology continues to evolve, embracing such innovative approaches becomes instrumental in staying at the forefront of interactive and automated communication.

Top comments (2)

Collapse
 
thescarlettcoder profile image
Jeffrey

Nice work man, great explanations.
I enjoyed it

Collapse
 
emmanuelthecoder profile image
Emmanuel Aiyenigba • Edited

I'm glad you found the article helpful. I'd appreciate if you could give me a follow here.