diff --git a/example.js b/example.js index a674424..2c1e437 100644 --- a/example.js +++ b/example.js @@ -1,17 +1,25 @@ const { Client } = require('./index') const client = new Client({puppeteer: {headless: false}}); +// You can use an existing session and avoid scanning a QR code by adding a "session" object to the client options. +// This object must include WABrowserId, WASecretBundle, WAToken1 and WAToken2. client.initialize(); client.on('qr', (qr) => { + // NOTE: This event will not be fired if a session is specified. console.log('QR RECEIVED', qr); }); -client.on('authenticated', () => { - console.log('AUTHENTICATED'); +client.on('authenticated', (session) => { + console.log('AUTHENTICATED', session); }); +client.on('auth_failure', msg => { + // Fired if session restore was unsuccessfull + console.error('AUTHENTICATION FAILURE', msg); +}) + client.on('ready', () => { console.log('READY'); }); diff --git a/src/Client.js b/src/Client.js index 319d11e..b0480b2 100644 --- a/src/Client.js +++ b/src/Client.js @@ -9,6 +9,8 @@ const ChatFactory = require('./factories/ChatFactory'); const Chat = require('./structures/Chat'); const Message = require('./structures/Message') +const fs = require('fs'); + /** * Starting point for interacting with the WhatsApp Web API * @extends {EventEmitter} @@ -31,18 +33,59 @@ class Client extends EventEmitter { const page = await browser.newPage(); page.setUserAgent(UserAgent); - await page.goto(WhatsWebURL); - await page.evaluate(ExposeStore); - - // Wait for QR Code - await page.waitForSelector('._1jjYO'); - const qr = await page.$eval('._2EZ_m', node => node.getAttribute('data-ref')); + if(this.options.session) { + await page.evaluateOnNewDocument ( + session => { + localStorage.clear(); + localStorage.setItem("WABrowserId", session.WABrowserId); + localStorage.setItem("WASecretBundle", session.WASecretBundle); + localStorage.setItem("WAToken1", session.WAToken1); + localStorage.setItem("WAToken2", session.WAToken2); + }, this.options.session); + } - this.emit(Events.QR_RECEIVED, qr); + await page.goto(WhatsWebURL); - // Wait for Auth - await page.waitForSelector('._2Uo0Z', {timeout: 0}); - this.emit(Events.AUTHENTICATED); + if(this.options.session) { + // Check if session restore was successfull + try { + await page.waitForSelector('._2Uo0Z', {timeout: 5000}); + } catch(err) { + if(err.name === 'TimeoutError') { + this.emit(Events.AUTHENTICATION_FAILURE, 'Unable to log in. Are the session details valid?'); + browser.close(); + + return; + } + + throw err; + } + + } else { + // Wait for QR Code + await page.waitForSelector('._1jjYO'); + const qr = await page.$eval('._2EZ_m', node => node.getAttribute('data-ref')); + this.emit(Events.QR_RECEIVED, qr); + + // Wait for code scan + await page.waitForSelector('._2Uo0Z', {timeout: 0}); + } + + await page.evaluate(ExposeStore); + + // Get session tokens + const localStorage = JSON.parse(await page.evaluate(() => { + return JSON.stringify(window.localStorage); + })); + + const session = { + WABrowserId: localStorage.WABrowserId, + WASecretBundle: localStorage.WASecretBundle, + WAToken1: localStorage.WAToken1, + WAToken2: localStorage.WAToken2 + } + + this.emit(Events.AUTHENTICATED, session); // Check Store Injection await page.waitForFunction('window.Store != undefined'); @@ -118,7 +161,6 @@ class Client extends EventEmitter { return ChatFactory.create(this, chat); } - } module.exports = Client; \ No newline at end of file diff --git a/src/util/Constants.js b/src/util/Constants.js index 2a86400..2ba1404 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -7,7 +7,8 @@ exports.UserAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit exports.DefaultOptions = { puppeteer: { headless: true - } + }, + session: false } exports.Status = { @@ -18,6 +19,7 @@ exports.Status = { exports.Events = { AUTHENTICATED: 'authenticated', + AUTHENTICATION_FAILURE: 'auth_failure', READY: 'ready', MESSAGE_CREATE: 'message', QR_RECEIVED: 'qr',