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