DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 968,547 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Emmanuel Aiyenigba
Emmanuel Aiyenigba

Posted 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);
  }
Enter fullscreen mode Exit fullscreen mode

}

})

module.exports = bot;
`

Hey, one more thing. Check below.

.

.

.

Thanks for reading! I figured I should say that here. Ahahahaha!

Top comments (0)

Need a better mental model for async/await?

Check out this classic DEV post on the subject.

β­οΈπŸŽ€ JavaScript Visualized: Promises & Async/Await

async await