Introduction
In this guide, we will explain how to develop a WhatsApp bot on Node JS using our WhatsApp API gateway.
In our example, we will try to cover the basic, most frequently used functionality, such as:
- Responding to commands
- Outputting the current chat ID (in either private or group messages)
- Outputting the name of the user chatting with the bot
- Sending files of different formats (pdf, jpg, doc, mp3, etc.)
- Sending voice messages (*.ogg files)
- Sending GPS coordinates (latitude and longitude)`
- Creating a conference (group) chatbot
An important note: Since the whole system works via WhatsAppWeb, your phone must always be connected to the internet.
A Bit of Theory
TheChat-Api service allows for setting up a webhook that will send information about new messages (and more) to your webserver.
Since the webserver will be installed on your computer/VPS/VDS (hereafter host), you will need to specify the address for the WebHook in your user account (https://app.chat-api.com/).
Go to your account settings and specify the address. In our example, it is https://subdomain.domain.com/webhook.
If you use a separate domain, it will look something like this: https://domain.com/webhook.
After you hit Save, all notifications about new messages will go to your address — all you'll have to do is process them.
WerecommendthatyoustartwithcloningourGitrepository so you could adaptitforyourselflater.
CreateaWhatsAppBotfolder andclone(or simply download) files from the Git repository into it.
Now use the npm install command to set the necessary dependencies (libraries) for your bot.
Next, go to the config.js file and specify your URL for requests (in quotation marks) and your token — you can get them in your user account. This is how it should look:
module.exports = {
apiUrl: "https://eu14.chat-api.com/instance12345/",
token: "xb1s668fphzmr9m"
}
Save the file and type node index.js in the console — this command will launch the webserver so it could process requests.
If you do everything right, the bot will work.
Now let's look at how to developabotfromscratch.
Since you are reading this guide, we assume that you know how to install Node.JS and NPM, so we will skip this part and get straight to the point.
Create a WhatsAppBot folder, go there and open the terminal. Initialize a project by using the npm init command, fill out the information asked or just click Enter a few times until you see a dialog box that says Is this OK? (Yes). Press Enter again to confirm initializing.
After that, create a main file (index.js) that will contain basic bot logic. You will also need to create a config . js filewhere you will add the main parameters. Since these parameters can be dynamic, you won't have to edit the main file every time you need to modify them.
Open the config . js file and add two parameters there — _apiUrl_and token:
module.exports = {
apiUrl: "",
token: ""
}
Don't forget about the first line — module.exports — it will allow you to access these parameters from another file.
You can enter the parameters from your user account into the config . js file right away. Once you're done, save and close it.
In our example, we will use the following requirements: Express (https://www.npmjs.com/package/express) Node-Fetch (https://www.npmjs.com/package/node-fetch) Body-Parser (https://www.npmjs.com/package/body-parser)
To start creating a bot, open_index . js_ and declare the dependencies:
const config = require("./config.js");
const token = config.token, apiUrl = config.apiUrl;
const app = require('express')();
const bodyParser = require('body-parser');
const fetch = require('node-fetch');
Let us expand on this a bit. The node-fetch allows sending requests to the API, the config file loads data from other files, and assigning values to the token and apiUrl variables from the config.js file helps ease access to them. The Express module is used to set up a webserver while the body-parser helps to easily extract incoming requests.
Next, let your parser know that you are going to work with JSON data:
app.use(bodyParser.json());
Justincase, add the full errorhandler to observe errors that can appear while processing requests:
process.on('unhandledRejection', err => {
_console_.log(err)
});
Next up, start writing the base code. To check the domain parked to the host, you need to generate the main page (a kind of an index . html) with the following block:
app.get('/', function (req, res) {
res.send("It's working");
});
Simply put, it is a way to check the work of your website. After you have launched the project, go to yoursite . ru, and, if you have done everything right, you'll see the words It's working. Now it's time to write the function for communicating with our API.
async function apiChatApi(method, params){
_const_ options = {};
options['method'] = "POST";
options['body'] = JSON.stringify(params);
options['headers'] = { 'Content-Type': 'application/json' };
_const_ url = `${apiUrl}/${method}?token=${token}`;
_const_ apiResponse = await fetch(url, options);
_const_ jsonResponse = await apiResponse.json();
return jsonResponse;
}
Let'slookatitmoreclosely. First, you need to createtheapiChatApi asynchronousfunctionthatwillhavetwoparameters: the method called, and the parameter object used to call the method. To give you a rough idea, when you want to send a message, you call the message method and pass the message text and sender as parameters.
Next,inside the function, create an option object. Add the json_and_method keys to the object. Json is used to send values required for the API, while method specifies the method you use to call and get the response.
Once you're done,definetheconstantvalue of yourURLfortheAPI. This will include the URL itself (from the config file), the method and the token passed via a GET request.
Afterthat, sendtherequestandget the responseinapiResponse(by the way, with a simple bot, you won't really need a function response except for detecting errors).
Now that the API communication function is ready, it's time to write the logic for the bot.
Choose the name for your processing page. In our case, it's webhook (since requests to the address http://yoursite.com/webhook will be sent by a webhook).
Write a handler for your URL:
app.post('/webhook', async function (req, res) {
});
Insidethehandler, save everythingyouwillget into the _data_variable:
app.post('/webhook', async function (req, res) {
const data = req.body;
});
Thenparseallthe messages:
app.post('/webhook', async function (req, res) {
_const_ data = req.body;
for (_var_ i in data.messages) {
}
});
Next, addinformationaboutincomingmessagestovariablesandskipinformationaboutoutgoingmessages:
app.post('/webhook', async function (req, res) {
_const_ data = req.body;
for (_var_ i in data.messages) {
_const_ author = data.messages[i].author;
_const_ body = data.messages[i].body;
_const_ chatId = data.messages[i].chatId;
_const_ senderName = data.messages[i].senderName;
if(data.messages[i].fromMe)return;
}
});
Now theinformationabouttheauthor is presented in the author variable, body contains the text, chatId — the Id of the current chat, and senderName — the name of the person you are chatting with.
Don't forget to add the code for launching the webserver at the end of the file:
app.listen(80, function () {
_console_.log('Listening on port 80..');
});
Youcancheckthe work ofyourbotbywritingthefollowingcodeinthe_for_cycleafterthedeclaredvariables:
console.log(senderName, author, chatId, body);
Launchthebotby using the _node index. _js_commandandwritethe followingbot-message: Test._Ifeverythingis correct, here is what you'll see in the console: Eugene [email protected] [email protected] Test
The next step (provided everything works as it should) is to remove (orcommentout) thedebugginglinewiththe_console . log_. You'll also have to decide howyou willprocesscommands.
Whilethereareseveralwaystodoit, werecommend using the _if else if_constructionincombinationwithregular expressions because, it will allow you, first, to create complex commands with arguments; second, not to deal with duplicating variables (as was the case with switch – case); and, third, to very easily identify a wrongly entered command (with the last else) and display the corresponding prompt.
Next up, wearegoingtolookat the code nested in for. Don't get confused 😉
First of all, write the commands' structure:
if(/help/.test(body)){
}else if(/chatId/.test(body)){
}else if(/file (pdf|jpg|doc|mp3)/.test(body)){
}else if(/ptt/.test(body)){
}else if(/geo/.test(body)){
}else if(/group/.test(body)){
}
Next, move on to writing thecommand handlers. Start with help — this one is just a piece of cake.
All you have to do is write a previously prepared text in the text variable and assign the name of the user who wrote to the bot to the senderName variable.
As for the last line, it will include a call to the function working with the API where we pass the message method and the object with the parameters {chatId: chatId, body: text}.
To launch the project, you can use the command _node index._js and write help to the bot.
By the way, in case users write something your bot can't answer (like "Hi"__, etc.), we recommend sending them the text with all the available commands. In this case, users will always have this starting message within their reach.
Now, it's time to writeahandlerfor the command chatId. This is pretty easy, too:
await apiChatApi('message', {chatId: chatId, body: chatId});
Make a call totheAPI, the message method, and ask it to send the text chatId to the chat.
This was quite easy, wasn't it? Now, let's dive deeper.
You are in for the most complicated part of the code — the file command's functionality.
To begin with, have a good look at this line:
/file (pdf|jpg|doc|mp3)/.test(body)
In short, the idea of its logic is to check if the body value equals the file value + one of the values in the brackets. For example, whether the body value equals the file pdf value or the file jpg value, etc.
If it does, launch your handler. The first thing it'll do is parse the file type and put it to the fileType variable:
const fileType = body.match(/file (pdf|jpg|doc|mp3)/)[1];
Consequently, the value we will haveinthe_fileType_is pdf/jpg/doc/mp__3.
Now create an object with data to send:
const files = {
doc: "https://domain.com/tra.docx",
jpg: "https://domain.com/tra.jpg",
mp3: "https://domain.com/tra.mp3",
pdf: "https://domain.com/tra.pdf"
};
ItwillallowyoutogettheURLaddressofthefilebyaddressingits key index, forexample:
files["doc"] // => "https://domain.com/tra.docx"
files["mp3"] // => "https://domain.com/tra.mp3"
As a result, the files_[fileType]_ will return the URL of the file you need.
All that is left to do now is create an object of parameters to send to the API:
var dataFile = {
phone: author,
body: files[fileType],
filename: `FILE *.${fileType}`
};
The_phone_ variable will contain the information about the author of the message, the body — a link to the file (see the API) and the filename — a visible name of the file (in our example, it is the word FILE and its extension).
Whenever an image is requested, you need to add the caption key to the parameter object. Here is how to do it:
if(fileType == "jpg")dataFile['caption'] = "Photo txt.";
Now add it all to the function specifying that you want to call the sendFile method:
await apiChatApi('sendFile', dataFile);
Now implement the ptt command handler for voice messages.
await apiChatApi('sendAudio', {audio: "http://domain.com/tra.ogg", chatId: chatId});
Call to the function specifying the sendAudio method and the audio key and providing a direct link to the file in the parameter object.
In this guide, all links to files are static (they call to the hosting). We advise you to send files in the base64 format.
The_geo_commandhandlerisquite simple, too:
await apiChatApi('sendLocation', {lat: 51.178843, lng: -1.826210, address: 'Place', chatId: chatId});
Instead of audio, you need to send the lat and lng keys that stand for the latitude and longitude of the place, respectively. The address key must contain the address. As for the GPS coordinates, you can get them in Google Maps, for example.
Now we are left with the group command that is responsible for creating a group chatbot. Its handler is not much different from the several previous ones:
let arrayPhones = [ author.replace("@c.us","") ];
await apiChatApi('group', {groupName: 'Group with bot', phones: arrayPhones, messageText: 'Welcome!'});
Createan_arrayPhones_array. Add the author variable removing _@ c._us and leaving only the phone number in the line.
You can add several phone numbers at once.
Sendarequestto the function with the group method specifying the groupName keys — the name of the chat, the array of users in the phones, and the welcome text in the messageText.
Well, that's pretty much it. Your handlers are ready!