How to APIfy WhatsApp - Programmatic interaction with WhatsApp from Node using Playwright image 35

How to APIfy WhatsApp – Programmatic interaction with WhatsApp from Node using Playwright

Sending WhatsApp messages from services and applications has been a frequent request from our customers. It also was something I would have liked to use in workshops and demonstrations. It appeals to people. However, WhatsApp does not provide an open API for sending or receiving messages. There is a business service that companies can leverage – through global solution providers and provided a contract is in place. Too much fuzz for simple use cases – if you even can get in. There is a plan B: a very simple way to build your own API for WhatsApp – on top of the WhatsApp Web UI using a simple Node application and leveraging the Playwright browser automation library. Note that Playwright is also available for Python, Go, C# and Java.

An extensive introduction of Playwright can be read in my previous article What you could do if you had a free, tireless browser operator?! Introducing Playwright. In short: Playwright is an open source software library that gives us a deus in machina – a robot in our browser do perform power operations. This opens up opportunities from automated testing to tactical integration and RPA (Robotic Process Automation), and from deep

link bookmarks, screen scraping, web app health monitoring (smoketests) and customized web applications. Playwright is a fairly new (2020), open-source, JavaScript-based, cross-browser automation library for end-to-end testing, created by a team at Microsoft – that used to work at Google and created the popular Puppeteer tool.image

This article describes a Node application that runs an embedded browser through Playwright. This browser is navigated to the WhatsApp Web application. The Node application then programmatically locates the contact to which a message is to be sent in the web page, and types the message to be sent into the text box. Finally, it clicks on the send button to publish the message.

The Node application can handle many requests for sending messages. It would be easy to extend the module with additional functionality – for retrieving messages, for sending images, for sending messages to multiple contacts, for spellchecking and expanding acronyms and abbreviations, for translating and for listening for incoming messages and publishing events when they arrive. It would also be easy wrap the module in a true REST API that can be used in integrations and applications.

Creating the Node application to talk to WhatsApp Web

Creating a Node application that works with WhatsApp Web through Playwright requires of course a Node run time environment with Playwright installed. Then a Node application is created that navigates to WhatsApp Web and keeps the browser running for a long time.

image

[CODE language=”javascript”]
const { chromium } = require(‘playwright’);

const WHATSAPP_WEB_URL = “https://web.whatsapp.com/”

const sleep = (milliseconds) => {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
(async () => {
const browser = await chromium.launch({ headless: false })
const context = await browser.newContext()
const page = await context.newPage();

await page.goto(WHATSAPP_WEB_URL);

// inspect the user interface elements to manipulate for locating the contact, typing and sending tyhe message
// find the CSS selectors for the relevant elements

await sleep(50000000) // 1000* 50 seconds
await browser.close()
})()
[/CODE]When this application is executed, an incognito browser window opens and a QR code is presented. You need to scan this QR code in the WhatsApp app on your mobile phone – every time the Node application is restarted. image

I want to try to find out if in cookies or local storage perhaps the confirmation of the QR Code verification is stored and if so it can be initialized by the Node application so this manual step can be skipped.

After successful verification, the WhatsApp Web UI is opened. Now it is time to bring out the browser developer tools (ctrl+shift+i) and inspect the relevant elements to discover the CSS selectors. The next figure describes the selectors I determined:

image

Selector ‘._1awRl’  is used to locate the search field for entering the name of the contact. We use ‘span[title=”${whatsappContact}”]’ to find the element for the contact that can be clicked upon to bring up the list of recent messages in the right pane.

With ‘text=Type a message’ we find the box into which a new message can be entered. And finally  ‘button._2Ujuu’ is used to identify the Send button to be clicked to submit the message. With these four selectors – there is not much left to program:

  • enter contact name in search field
  • click on name of contact
  • enter message in text box
  • click on button to send message

The code that takes care of this is shown below:

[CODE language=”javascript”]
const { chromium } = require(‘playwright’);
const { trapEventsOnPage } = require(“../playwrightHelper”);

const WHATSAPP_WEB_URL = “https://web.whatsapp.com/”
const whatsappContact = “Mezelf, mij en ik”

const message = “My message from the WhatsApp robot”

const sleep = (milliseconds) => {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
(async () => {
const browser = await chromium.launch({ headless: false })
const context = await browser.newContext()
const page = await context.newPage();

await page.goto(WHATSAPP_WEB_URL);

// wait for element search box
await page.waitForSelector(‘._1awRl’)
// enter name of contact in search box
await page.fill(‘._1awRl’, whatsappContact);

// page filters list of contacts
await page.waitForSelector(`span[title=”${whatsappContact}”]`)
// click on the contact – this refreshes the right pane with recent messages and a box for sending new messages
await page.click(`span[title=”${whatsappContact}”]`)

// wait for the field to send a message
await page.waitForSelector(‘text=Type a message’)
// type the message to send
await page.type(‘text=Type a message’, message)
// click button to send message
await page.click(‘button._2Ujuu’)
await browser.close()
})()

[/CODE]

Resources

Code for this article on GitHub: https://github.com/lucasjellema/playwright-scenarios/tree/main/whatsapp 

My earlier article: An extensive introduction of Playwright What you could do if you had a free, tireless browser operator?! Introducing Playwright