diff --git a/.gitignore b/.gitignore
index cab28c7..f2c087c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
node_modules
-whatsapp-session.json
\ No newline at end of file
+whatsapp-session-*.json
\ No newline at end of file
diff --git a/app-multiple-device.js b/app-multiple-device.js
new file mode 100644
index 0000000..5e062bf
--- /dev/null
+++ b/app-multiple-device.js
@@ -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);
+});
diff --git a/index.html b/index.html
index 4ab95ca..c2d1d88 100644
--- a/index.html
+++ b/index.html
@@ -2,15 +2,43 @@
Whatsapp API by Ngekoding
+
Whatsapp API
Powered by Ngekoding
-
![QR Code]()
-
Logs:
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![QR Code]()
+
Logs:
+
+
+
@@ -19,21 +47,38 @@
$(document).ready(function() {
var socket = io();
- socket.on('message', function(msg) {
- $('.logs').append($('').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($('').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();
});
});
diff --git a/nodemon.json b/nodemon.json
index 66dd8ab..cf1c318 100644
--- a/nodemon.json
+++ b/nodemon.json
@@ -1,3 +1,3 @@
{
- "ignore": ["whatsapp-session.json"]
+ "ignore": ["whatsapp-session-*.json"]
}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 04f75ee..1f6f986 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -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",
diff --git a/package.json b/package.json
index 9d227ab..fe82d80 100644
--- a/package.json
+++ b/package.json
@@ -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"
diff --git a/readme.md b/readme.md
index 13e94be..5553276 100644
--- a/readme.md
+++ b/readme.md
@@ -8,6 +8,7 @@ Watch the tutorials:
- Whatsapp API Tutorial: Tips & Tricks
- Whatsapp API Tutorial: Sending Media File
- Whatsapp API Tutorial: Deploy to Heroku
+- Whatsapp API Tutorial: Multiple Device
### How to use?
- Clone or download this repo