npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, πŸ‘‹, I’m Ryan HefnerΒ  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you πŸ™

Β© 2024 – Pkg Stats / Ryan Hefner

@dinamicadigitalsas/automate

v0.0.7

Published

πŸ’¬ πŸ€– The most advanced NodeJS whatsapp library for chatbots with advanced features.

Downloads

5

Readme

wa-automate-nodejs

wa-automate-nodejs is the most advanced NodeJS library which provides a high-level API control to Whatsapp.

It is built using puppeteer, based on this python wrapper.

npm version node Downloads Average time to resolve an issue Percentage of issues still open

WhatsApp_Web 2.2017.6

Buy me a coffee Consulting Request

Installation

> npm i --save @open-wa/wa-automate

Usage

// import { create, Whatsapp } from '@open-wa/wa-automate';
const wa = require('@open-wa/wa-automate');

wa.create().then(client => start(client));

function start(client) {
  client.onMessage(message => {
    if (message.body === 'Hi') {
      client.sendText(message.from, 'πŸ‘‹ Hello!');
    }
  });
}
After executing create() function, @open-wa/wa-automate will create an instance of whatsapp web. If you are not logged in, it will print a QR code in the terminal. Scan it with your phone and you are ready to go!
@open-wa/wa-automate will remember the session so there is no need to authenticate everytime.

Latest Changes

With the constant updates from Whatsapp. It is advisable to always use the latest version of @open-wa/wa-automate.

Functions list

| Function | Description | Implemented | | --------------------------------- | ----------- | ----------- | | Receive message | | βœ… | | Automatic QR Refresh | | βœ… | | Send text | | βœ… | | Get contacts | | βœ… | | Get chats | | βœ… | | Get groups | | βœ… | | Get group members | | βœ… | | Send contact | | βœ… | | Get contact detail | | βœ… | | Send Images (image) | | βœ… | | Send media (audio, doc) | | βœ… | | Send media (video) | | βœ… | | Send stickers | |βœ… | | Decrypt media (image, audio, doc) | | βœ… | | Capturing QR Code | | βœ… | | Multiple Sessions | | βœ… | | [Last seen & isOnline (beta)] | | βœ… | | πŸ“ SEND LOCATION!! (beta) | | βœ… | | Simulated '...typing' | | βœ… | | Send GIFs! | | βœ… | | Forward Messages | | βœ… | | Listen to Read Receipts | | βœ… | | Listen to Live Locations | | βœ… | | Group participant changes | | βœ… | | Create Groups | | βœ… | | add, remove, promote, demote participants | | βœ… |

Checkout all the available functions here.

Insiders Program

open-wa is at the forefront of open source WhatsApp development. Some new features will be initially released through the insiders program and then released for general availability (GA) the following month. The following features are currently for insiders:

| Function | Description | GA Release Date | | --------------------------------- | ----------- | ----------- | | setGroupToAdminsOnly | Changes group setting so only admins can send messages | 1st May 2020| | setGroupEditToAdminsOnly | Changes group setting so only admins can edit group info | 1st May 2020|

Insiders need a license key.

Starting a conversation

There are 3 ways to start a chat with a new number:

  1. WhatsApp Links

    You can send a special link to the person you want to start a chat with. This will open a conversation with your number on their phone. This way you can insure that they have explicitly started a conversation with you.

  2. WhatsApp Buttons

    You can add this button to your website which, when clicked, will open a chat with you in the same way as above.

  3. With a License Key

    In order to unlock the functionality to send texts to unknown numbers through @open-wa/wa-automate itself, you will need an License key.

    One License Key is valid for one number. Each License Key is Β£5 per month or Β£50 per year. Instructions below.

License Key

For now the process happens through Buy Me A Coffee (BMAC)

How to get an License key:

  1. Go to BMAC.

  2. Click on 'Membership'. For the insiders program, you do not need to get a membership.

  3. Select your payment preference (monthly/annually).

  4. Add the phone number you want to assign to the License Key in the notes, along with the use case for this functionality and your github username

  5. Select "Make this message private."

  6. Complete the process for membership.

  7. You will receive your License key via email.

  8. Add licenseKey to your config:

...
create({
  licenseKey: "..."
})
...

Notes:

  • You can change the number assigned to a specific License Key at any time.
  • In order to cancel your License Key, simply stop your membership.
  • Apart from adding your licenseKey to your config, you will need to change nothing else in your code.
  • An added benefit for members is priority on issues.
  • License Keys for the insiders program expire after a month when the feature becomes GA.
  • License Key request may be rejected.

Running the demo

You can clone this repo and run the demo, but you will need to use typescript/ts-node:

> git clone https://github.com/open-wa/wa-automate-nodejs.git
> cd wa-automate-nodejs
> npm i
> npm i -g ts-node typescript
> cd demo
> ts-node index.ts

Capturing QR Code

An event is emitted every time the QR code is received by the system. You can grab hold of this event emitter by importing ev

import { ev } from '@open-wa/wa-automate';
const fs = require('fs');

ev.on('qr.**', async qrcode => {
  //qrcode is base64 encoded qr code image
  //now you can do whatever you want with it
  const imageBuffer = Buffer.from(
    qrcode.replace('data:image/png;base64,', ''),
    'base64'
  );
  fs.writeFileSync('qr_code.png', imageBuffer);
});

You can see a live implementation of this on demo/index.ts. Give it a spin! :D

Refreshing QRCode

In version v1.6.13^, you can now refresh the QR code every 10 seconds (you can change the interval).

create({
    autoRefresh:false, //default to true
    qrRefreshS:30 //please note that if this is too long then your qr code scan may end up being invalid. Generally qr codes expire every 15 seconds.
}).then(async client => await start(client));

Kill the session

As of v1.6.6^ you can now kill the session when required. Best practice is to manage trycatch-es yourself and kill the client on catch.

try{
...
await client.sendMessage(...
...
} catch(error){
client.kill();
//maybe restart the session then
}

Force Refocus and reacting to state

When a user starts using whatsapp web in a different browser, @open-wa/wa-automate will be left on a screen prompting you to click 'Use here'. As of v1.6.6^ you can now force the client to press 'Use here' everytime the state has changed to 'CONFLICT'. onStateChanged results in 'UNPAIRED', 'CONNECTED' or 'CONFLICT';

client.onStateChanged(state=>{
    console.log('statechanged', state)
    if(state==="CONFLICT") client.forceRefocus();
  });

Decrypting Media

Here is a sample of how to decrypt media. This has been tested on images, videos, documents, audio and voice notes.

import { create, Whatsapp, decryptMedia } from '@open-wa/wa-automate';
const mime = require('mime-types');
const fs = require('fs');

function start(client: Whatsapp) {
  client.onMessage(async message => {
    if (message.mimetype) {
      const filename = `${message.t}.${mime.extension(message.mimetype)}`;
      const mediaData = await decryptMedia(message);
      const imageBase64 = `data:${message.mimetype};base64,${mediaData.toString(
        'base64'
      )}`;
      await client.sendImage(
        message.from,
        imageBase64,
        filename,
        `You just sent me this ${message.type}`
      );
      fs.writeFile(filename, mediaData, function(err) {
        if (err) {
          return console.log(err);
        }
        console.log('The file was saved!');
      });
    }
  });
}

create().then(client => start(client));

It is always preferable to keep projects smaller than needed so you can now use a lightweight library called wa-decrypt for projects that do not need all of @open-wa/wa-automate.

You can install that using:

> npm i --save wa-decrypt

and import it like so:

import { decryptMedia } from 'wa-decrypt';

Learn more about wa-decrypt here

Issues with decyption

If you are having issues with decryption it may be due to the user agent being used by the decrypt method. You can remedy this by passing a custom user agent as a second parameter to the decrypt method. Now there is a convenience method on the WhatsApp class to allow you to easily get a compatible user agent shown below. This feature is available in v.1.5.8 and above.

...
      const generatedUa = await client.getGeneratedUserAgent(); //you can optionally pass your custom user agent in here also getGeneratedUserAgent('...');
      const mediaData = await decryptMedia(message,generatedUa);
...

Sending Media/Files

Here is a sample of how to send media. This has been tested on images, videos, documents, audio and voice notes.

Interestingly sendImage has always worked for sending any type of file.

An example of sending a is shown in the Decrypting Media secion above also.

import { create, Whatsapp} from '@open-wa/wa-automate';

function start(client: Whatsapp) {
await client.sendFile('[email protected]',[BASE64 FILE DATA],'some file.pdf', `Hello this is the caption`);
}

create().then(client => start(client));

Please note sometimes short(<4s) voice notes sometimes do not decrypt properly and result in empty audio files.

Sending Video

If you intend to use video via @open-wa/wa-automate, you need to use a chrome instance with puppeteer instead of the default chromium instance. This is becase chromium does not have any relevant video codecs needed for new whatsapp web video sending features.

You will need to make sure that you have a valid chrome instance on your machine then use the following to tell puppeteer where it can find your chrome isntance. The below demo is an example for mac & windows. For linux based hosts, you can find the chrome path with whereis google-chrome, it should be something like /usr/bin/google-chrome


create({
  // For Mac:
  executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
  // For Windows:
  // executablePath: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
}).then(client => start(client));

Sending Gifs

Extending the functionality of sending videos, version 1.4.2 brings with it the ability to send GIFs!!!! This was super annoying to figure out, as I was sent on a wild goose chase but it turned out that the answer was just 2 simple lines.

There are two ways to send GIFs - by Video or by giphy link.

  1. Sending Video as a GIF.

WhatsApp doesn't actually support the .gif format - probably due to how inefficient it is as a filetype - they instead convert GIFs to video then process them.

In order to send gifs you need to do the same (convert the gif to an mp4 file) then use the following method:


import { create, Whatsapp} from '@open-wa/wa-automate';

function start(client: Whatsapp) {
await client.sendVideoAsGif('[email protected]',[BASE64 Video FILE DATA],'some file.mp4', `Hello this is the caption`);
}

///IMPORTANT! Please make sure to point to your chrome installation and make sure your host has ffmpeg support
create({
  // For Mac:
  executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
  // For Windows:
  // executablePath: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
}).then(client => start(client));
  1. Sending a Giphy Media Link

This is a convenience method to make it easier to send gifs from the website GIPHY. You need to make sure you use a giphy media link as shown below.


import { create, Whatsapp} from '@open-wa/wa-automate';

function start(client: Whatsapp) {
await client.sendGiphy('[email protected]','https://media.giphy.com/media/oYtVHSxngR3lC/giphy.gif', `Hello this is the caption`);
}

create({
  executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
}).then(client => start(client));

Sending Location

As of version 1.3.0 you can now send location!! You can't even do this in normal whatsapp web interface.

You need to pass the following params:

Here's how you do it:

await client.sendLocation('[email protected]', '51.5074', '0.1278',  'LONDON!')

Simulate typing

As of version 1.3.1 you can now simulate '...typing'

You need to pass the following params:

Note: You need to manually turn this off!!!

//start '...typing'
await client.simulateTyping('[email protected]',true)
//wait 3 seconds

//stop '...typing'
await client.simulateTyping('[email protected]',false)

Load profile pics from server

Generally, after the 20th chat in your whatsapp, getChat methods do not retreive the chat picture. You need to get these from the WhatsApp servers. This is how you do it in v1.6.6^:

client.getProfilePicFromServer('[email protected]')

Forward Messages

As of version 1.5.1 you can now forward messages. This has been tested on most types of messages.

You need to pass the following params:

  • chat to forward messages to : [email protected]
  • messages: a single or array of message ids or message objects
  • skipMyMessages: true or false, if true it will filter out messages sent by you from the list of messages, default false.

Note: You need to manually turn this off!!!

//forward messages
await client.forwardMessages('[email protected]',[...],true)

//forward single message by id
await client.forwardMessages('[email protected],"...",true)

Reply to messages

As of version 1.6.17, you can now reply to specific messages.

...
/**
   * @param to string chatid
   * @param content string reply text
   * @param quotedMsg string | Message the msg object or id to reply to.
   */

      await client.reply('[email protected]','This is the reply',message);
...

Create group

As of v1.7.2 you can now create a new group. The first parameter is the group name, the second parameter is the contact ids to add as participants

...
  client.createGroup('Cool new group','[email protected]') //you can also send an array of ids.
...

Group participants [beta]

As of v1.7.0 you can now add, remove, promote & demote participants for groups. The first parameter is the chat id for the group. The second parameter is the number to which you are conducting the action.

...
  client.addParticipant('[email protected]','[email protected]')
  client.removeParticipant('[email protected]','[email protected]')
  client.promoteParticipant('[email protected]','[email protected]')
  client.demoteParticipant('[email protected]','[email protected]')
...

Group participant changes

As of version 1.5.6 you can now listen in on changes to group participants. You can react to when participants are added and removed.

client.onParticipantsChanged("[email protected]", (participantChangedEvent:any) => console.log("participant changed for group", participantChangedEvent));

//participantChangedEvent returns
{
  by: '[email protected]', //who performed the action
  action: 'remove',
  who: [ '[email protected]' ] //all the numbers the action effects.
}

This solution can result in some false positives and misfires however a lot of effort has been made to mitigate this to a reasonable level. Best practice is to maintian a seperate registry of participants and go from that.

Listen to Live Locations

As of version 1.7.21 you can now listen to live locations from a specific chat. You can see the liveLocation callback object here


client.onLiveLocation('[email protected]', (liveLocation) => {
  console.log('Someone moved',liveLocation)
})

Listen to Read Receipts

As of version 1.5.3 you can now listen in on the read state (or technically acknowledgement state) of the messages. As of writing the limitation is presumed to be on sent messages.

The callback you set returns the whole raw message object.

Here's how you do it.

client.onAck((msg:any) => console.log(msg.id.toString(),msg.body,msg.ack))

ack represents the acknoledgement state, of which there are 3.

1 => Message Sent (1 tick)

2 => Message Received by Recipient (2 ticks)

3 => Message Read Receipt Confirmed (2 blue ticks)

Note: You won't get 3 if the recipient has read receipts off.

Timing out an unpaired session

If you want to kill the process after a certain amount of seconds due to an unscanned code, you can now set the killTimer parameter in the configuration object.

create({
  killTimer: 30 //kills the session if the QR code is not scanned within 30 seconds.
})
.then(client => start(client));

Managing multiple sessions at once

With v1.2.4, you can now run multiple sessions of @open-wa/wa-automate in the same 'app'. This allows you to do interesting things for example:

  1. Design and run automated tests for you WA bot.
  2. Connect two or more whatsapp numbers to a single (or multiple) message handler(s)
  3. Use one client to make sure another one is alive by pinging it.

Please see demo/index.ts for a working example

NOTE: DO NOT CREATE TWO SESSIONS WITH THE SAME SESSIONID. DO NOT ALLOW SPACES AS SESSION ID.

import { create, Whatsapp} from '@open-wa/wa-automate';

function start(client: Whatsapp) {
  ...
}

create().then(client => start(client));

create({
  sessionId:'another_session'
}).then(client => start(client));

You can then capture the QR Code for each session using the following event listener code:

//events are fired with the ev namespace then the session Id. e.g "qr.another_session"
//You can however use the wildcard operator with the new event listener and capture the session Id as a parameter instead.
ev.on('qr.**', async (qrcode,sessionId) => {
  console.log("TCL: qrcode,sessioId", qrcode,sessionId)
  //base64 encoded qr code image
  const imageBuffer = Buffer.from(qrcode.replace('data:image/png;base64,',''), 'base64');
  fs.writeFileSync(`qr_code${sessionId?'_'+sessionId:''}.png`, imageBuffer);
});

Manage page errors

Since this project is built upon puppeteer, you can access the Puppeteer Page instance by referencing client.page, and then therefore you can listen to any errors on the page like so:

client.page.on('error', _=>{
...
}

Custom Set Up

With v.1.2.6 you can now forward custom arguments through the library to puppeteer. This includes any overrides to the pupeteer config and a custom useragent.

Note: If you want to change the user agent but leave the puppeteer config the same then just pass {} to the pupeteer config. Also if you don't want to use a custom session then just use 'session' for the first argument.

As with session name segment, these are all optional parameters.

Why should you use a custom user agent?

Users of these whatsapp injection libraries should use different user agents (preferably copy the one you have one your own pc) because then it makes it harder for whatsapp to break the mecahnism to restart sessions for this library.

Setting up your client in headless:false mode ensures you can easily visually debug any issues.

Example:

import { create, Whatsapp} from '@open-wa/wa-automate';

function start(client: Whatsapp) {
  ...
}

create().then(client => start(client));

//1st argument is the session name
//2nd argument is the puppeteer config override
//3rd argument is the user agent override

create({
  headless: false,
  customUserAgent: 'some custom user agent'
})
.then(client => start(client));

Best Practice

Since this is not an officially sanctioned solution it is tempermental to say the least. Here are some best practices:

  1. Keep the session alive
  2. Offload most work off of your @open-wa/wa-automate setup (i.e forward all events to a pubsub or something)
  3. Keep the phone nearby just in case you need to reauthenticate
  4. Use a chrome instance instead of the default chromium instance
  5. Use headless: false for easy & quick visual debugging
  6. Implement the unread messages functionality on creation of a session so you don't miss any messages upon any downtime.
  7. Implement a promise-queue
  8. Use a unique and valid custom user-agent
  9. await all @open-wa/wa-automate methods just in case
  10. Do not run your s@open-wa/wa-automate instance on a Windows machine.
  11. Always kill the session safely upon error or SIGINT.
import { create, Whatsapp} from '@open-wa/wa-automate';
const { default: PQueue } = require("p-queue");

const queue = new PQueue({
  concurrency: 4,
  autoStart:false
   });

const proc = async message => {
  //do something with the message here
    console.log(message)
    return true;
}

const processMessage = message => queue.add(proc(message));

async function start(client: Whatsapp) {
  const unreadMessages = await client.getAllUnreadMessages();
  unreadMessages.forEach(processMessage)
  ...
  await client.onMessage(processMessage);
  queue.start();
}

create().then(client => start(client));

//1st argument is the session name
//2nd argument is the puppeteer config override
//3rd argument is the user agent override

create({
  // For Mac:
  executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
  // For Windows:
  // executablePath: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
  headless: false,
  autoRefresh:true,
  customUserAgent: 'some custom user agent'
})
.then(client => start(client));

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

License

Hippocratic + Do Not Harm Version 1.0

Legal

This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by WhatsApp or any of its affiliates or subsidiaries. This is an independent and unofficial software. Use at your own risk.

Cryptography Notice

This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. See http://www.wassenaar.org/ for more information.

The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing cryptographic functions with asymmetric algorithms. The form and manner of this distribution makes it eligible for export under the License Exception ENC Technology Software Unrestricted (TSU) exception (see the BIS Export Administration Regulations, Section 740.13) for both object code and source code.