Add new feature: multiple whatsapp client (dynamic)

This commit is contained in:
Nur Muhammad
2020-12-10 21:31:14 +08:00
parent d2ac747c0b
commit f67f3b8d61
7 changed files with 261 additions and 41 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,2 @@
node_modules
whatsapp-session.json
whatsapp-session-*.json

174
app-multiple-device.js Normal file
View File

@@ -0,0 +1,174 @@
const { Client, MessageMedia } = require('whatsapp-web.js');
const express = require('express');
const socketIO = require('socket.io');
const qrcode = require('qrcode');
const http = require('http');
const fs = require('fs');
const { phoneNumberFormatter } = require('./helpers/formatter');
const axios = require('axios');
const port = process.env.PORT || 8000;
const app = express();
const server = http.createServer(app);
const io = socketIO(server);
app.use(express.json());
app.use(express.urlencoded({
extended: true
}));
app.get('/', (req, res) => {
res.sendFile('index.html', {
root: __dirname
});
});
const sessions = [];
const createSession = function(id, description, io) {
const SESSION_FILE_PATH = `./whatsapp-session-${id}.json`;
let sessionCfg;
if (fs.existsSync(SESSION_FILE_PATH)) {
sessionCfg = require(SESSION_FILE_PATH);
}
const client = new Client({
restartOnAuthFail: true,
puppeteer: {
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--no-first-run',
'--no-zygote',
'--single-process', // <- this one doesn't works in Windows
'--disable-gpu'
],
},
session: sessionCfg
});
client.initialize();
client.on('qr', (qr) => {
console.log('QR RECEIVED', qr);
qrcode.toDataURL(qr, (err, url) => {
io.emit('qr', { id: id, src: url });
io.emit('message', { id: id, text: 'QR Code received, scan please!' });
});
});
client.on('ready', () => {
io.emit('ready', { id: id });
io.emit('message', { id: id, text: 'Whatsapp is ready!' });
});
client.on('authenticated', (session) => {
io.emit('authenticated', { id: id });
io.emit('message', { id: id, text: 'Whatsapp is authenticated!' });
sessionCfg = session;
fs.writeFile(SESSION_FILE_PATH, JSON.stringify(session), function(err) {
if (err) {
console.error(err);
}
});
});
client.on('auth_failure', function(session) {
io.emit('message', { id: id, text: 'Auth failure, restarting...' });
});
client.on('disconnected', (reason) => {
io.emit('message', { id: id, text: 'Whatsapp is disconnected!' });
fs.unlinkSync(SESSION_FILE_PATH, function(err) {
if(err) return console.log(err);
console.log('Session file deleted!');
});
client.destroy();
client.initialize();
});
// Tambahkan client ke sessions
sessions.push({
id: id,
description: description,
client: client
});
}
// Socket IO
io.on('connection', function(socket) {
socket.on('create-session', function(data) {
console.log('Create session: ' + data.id);
createSession(data.id, data.description, io);
});
});
// io.on('connection', function(socket) {
// socket.emit('message', 'Connecting...');
// client.on('qr', (qr) => {
// console.log('QR RECEIVED', qr);
// qrcode.toDataURL(qr, (err, url) => {
// socket.emit('qr', url);
// socket.emit('message', 'QR Code received, scan please!');
// });
// });
// client.on('ready', () => {
// socket.emit('ready', 'Whatsapp is ready!');
// socket.emit('message', 'Whatsapp is ready!');
// });
// client.on('authenticated', (session) => {
// socket.emit('authenticated', 'Whatsapp is authenticated!');
// socket.emit('message', 'Whatsapp is authenticated!');
// console.log('AUTHENTICATED', session);
// sessionCfg = session;
// fs.writeFile(SESSION_FILE_PATH, JSON.stringify(session), function(err) {
// if (err) {
// console.error(err);
// }
// });
// });
// client.on('auth_failure', function(session) {
// socket.emit('message', 'Auth failure, restarting...');
// });
// client.on('disconnected', (reason) => {
// socket.emit('message', 'Whatsapp is disconnected!');
// fs.unlinkSync(SESSION_FILE_PATH, function(err) {
// if(err) return console.log(err);
// console.log('Session file deleted!');
// });
// client.destroy();
// client.initialize();
// });
// });
// Send message
app.post('/send-message', (req, res) => {
const sender = req.body.sender;
const number = phoneNumberFormatter(req.body.number);
const message = req.body.message;
const client = sessions.find(sess => sess.id == sender).client;
client.sendMessage(number, message).then(response => {
res.status(200).json({
status: true,
response: response
});
}).catch(err => {
res.status(500).json({
status: false,
response: err
});
});
});
server.listen(port, function() {
console.log('App running on *: ' + port);
});

View File

@@ -2,15 +2,43 @@
<html>
<head>
<title>Whatsapp API by Ngekoding</title>
<style>
.client {
border: 1px solid #ccc;
padding: 20px;
box-sizing: border-box;
display: inline-block;
margin: 10px;
}
.hide {
display: none;
}
</style>
</head>
<body>
<div id="app">
<h1>Whatsapp API</h1>
<p>Powered by Ngekoding</p>
<img src="" alt="QR Code" id="qrcode">
<h3>Logs:</h3>
<ul class="logs"></ul>
<div class="form-container">
<label for="client-id">ID</label><br>
<input type="text" id="client-id" placeholder="Masukkan ID">
<br><br>
<label for="client-description">Deskripsi</label><br>
<textarea rows="3" id="client-description" placeholder="Masukkan deskripsi"></textarea>
<br><br>
<button class="add-client-btn">Tambah Client</button>
</div>
<hr>
<div class="client-container">
<div class="client hide">
<h3 class="title"></h3>
<p class="description"></p>
<img src="" alt="QR Code" id="qrcode">
<h3>Logs:</h3>
<ul class="logs"></ul>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>
@@ -19,21 +47,38 @@
$(document).ready(function() {
var socket = io();
socket.on('message', function(msg) {
$('.logs').append($('<li>').text(msg));
// Ketika button tambah diklik
$('.add-client-btn').click(function() {
var clientId = $('#client-id').val();
var clientDescription = $('#client-description').val();
var template = $('.client').first().clone()
.removeClass('hide')
.addClass(clientId);
template.find('.title').html(clientId);
template.find('.description').html(clientDescription);
$('.client-container').append(template);
socket.emit('create-session', {
id: clientId,
description: clientDescription
});
});
socket.on('qr', function(src) {
$('#qrcode').attr('src', src);
$('#qrcode').show();
socket.on('message', function(data) {
$(`.client.${data.id} .logs`).append($('<li>').text(data.text));
});
socket.on('qr', function(data) {
$(`.client.${data.id} #qrcode`).attr('src', data.src);
$(`.client.${data.id} #qrcode`).show();
});
socket.on('ready', function(data) {
$('#qrcode').hide();
$(`.client.${data.id} #qrcode`).hide();
});
socket.on('authenticated', function(data) {
$('#qrcode').hide();
$(`.client.${data.id} #qrcode`).hide();
});
});
</script>

View File

@@ -1,3 +1,3 @@
{
"ignore": ["whatsapp-session.json"]
"ignore": ["whatsapp-session-*.json"]
}

54
package-lock.json generated
View File

@@ -25,9 +25,9 @@
}
},
"@types/node": {
"version": "14.14.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.6.tgz",
"integrity": "sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==",
"version": "14.14.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz",
"integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==",
"optional": true
},
"@types/yauzl": {
@@ -550,9 +550,9 @@
"dev": true
},
"debug": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"requires": {
"ms": "2.1.2"
}
@@ -594,9 +594,9 @@
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"devtools-protocol": {
"version": "0.0.809251",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.809251.tgz",
"integrity": "sha512-pf+2OY6ghMDPjKkzSWxHMq+McD+9Ojmq5XVRYpv/kPd9sTMQxzEt21592a31API8qRjro0iYYOc3ag46qF/1FA=="
"version": "0.0.818844",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.818844.tgz",
"integrity": "sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg=="
},
"dicer": {
"version": "0.3.0",
@@ -827,11 +827,11 @@
}
},
"express-validator": {
"version": "6.6.1",
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.6.1.tgz",
"integrity": "sha512-+MrZKJ3eGYXkNF9p9Zf7MS7NkPJFg9MDYATU5c80Cf4F62JdLBIjWxy6481tRC0y1NnC9cgOw8FuN364bWaGhA==",
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.7.0.tgz",
"integrity": "sha512-sLnTFlyKEvesC2Fyn1TY4Q05cWCZHQQ1ijQOVbBG7hFeTKt4CNzttoF4t6CqrYroKa+2DOzj0E09odPIYDTbRg==",
"requires": {
"lodash": "^4.17.19",
"lodash": "^4.17.20",
"validator": "^13.1.1"
}
},
@@ -1593,12 +1593,12 @@
}
},
"puppeteer": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.4.1.tgz",
"integrity": "sha512-8u6r9tFm3gtMylU4uCry1W/CeAA8uczKMONvGvivkTsGqKA7iB7DWO2CBFYlB9GY6/IEoq9vkI5slJWzUBkwNw==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-5.5.0.tgz",
"integrity": "sha512-OM8ZvTXAhfgFA7wBIIGlPQzvyEETzDjeRa4mZRCRHxYL+GNH5WAuYUQdja3rpWZvkX/JKqmuVgbsxDNsDFjMEg==",
"requires": {
"debug": "^4.1.0",
"devtools-protocol": "0.0.809251",
"devtools-protocol": "0.0.818844",
"extract-zip": "^2.0.0",
"https-proxy-agent": "^4.0.0",
"node-fetch": "^2.6.1",
@@ -1995,14 +1995,14 @@
}
},
"tar-fs": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz",
"integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
"integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
"requires": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.0.0"
"tar-stream": "^2.1.4"
}
},
"tar-stream": {
@@ -2176,9 +2176,9 @@
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"validator": {
"version": "13.1.17",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.1.17.tgz",
"integrity": "sha512-zL5QBoemJ3jYFb2/j38y7ljhwYGXVLUp8H6W1nVxadnAOvUOytec+L7BHh1oBQ82/TzWXHd+GSaxUWp4lROkLg=="
"version": "13.5.1",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.5.1.tgz",
"integrity": "sha512-s+7LW1Xi0OzPNfGN7Hb2vk0YB/epp9KFHHGC5JtqZOE1dUkN4ULPFZAQ1inCu7ceAsWmOJu6sn9cnwm3R+ghWQ=="
},
"vary": {
"version": "1.1.2",
@@ -2186,9 +2186,9 @@
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"whatsapp-web.js": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/whatsapp-web.js/-/whatsapp-web.js-1.11.0.tgz",
"integrity": "sha512-gI9Lm1KmmBimNVL8yviJkfkI5qHaKBM47PYtTY2M97RityARmncLwXwss35aKLirQynOjpFxsFEVo4FhMB+3CQ==",
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/whatsapp-web.js/-/whatsapp-web.js-1.11.1.tgz",
"integrity": "sha512-tlRpZBLtBIqVvf9y6dCq3A7rbLu2ttc+D55JwCoNvPniiiZyFLVV4LUAA40g0hEpapkwFbiYdHv5eAV2Gubw3g==",
"requires": {
"@pedroslopez/moduleraid": "^4.1.0",
"jsqr": "^1.3.1",

View File

@@ -18,11 +18,11 @@
"axios": "^0.20.0",
"express": "^4.17.1",
"express-fileupload": "^1.2.0",
"express-validator": "^6.6.1",
"express-validator": "^6.7.0",
"http": "0.0.1-security",
"qrcode": "^1.4.4",
"socket.io": "^2.3.0",
"whatsapp-web.js": "^1.11.0"
"whatsapp-web.js": "^1.11.1"
},
"engines": {
"node": "v12.10.0"

View File

@@ -8,6 +8,7 @@ Watch the tutorials:
- <a href="https://youtu.be/uBu7Zfba1zA">Whatsapp API Tutorial: Tips & Tricks</a>
- <a href="https://youtu.be/ksVBXF-6Jtc">Whatsapp API Tutorial: Sending Media File</a>
- <a href="https://youtu.be/uSzjbuaHexk">Whatsapp API Tutorial: Deploy to Heroku</a>
- <a href="https://youtu.be/5VfM9PvrYcE">Whatsapp API Tutorial: Multiple Device</a>
### How to use?
- Clone or download this repo