feat: Multi-device support (#889)

* 🚑 Added ready selector for multi-device

* SendMessage fix

* File management system and some fixes

* cleanup

* cleanup again

* eslint

* critical fix for reloading the same session

* Checking for valid folder name (regex)

* ESLint hotfix (regex escapes)

* Typings cleanup

* cleanup listener

* Multi-device Branch merge (#888)

* Duplicate

* qr fix and allow non-beta users to connect

* urgent: selector fix

* urgent: qr timeout fix

* fix

* Updated type so no TS error when sending list/buttons

* Update index.d.ts

* fix QueryExist for Multidevice (#928)

* creates isRegisteredUserBeta

* fix QueryExist

* fix Error: GROUP_JID: invalid jid type: Not an instance of WID issue (#926)

* fix Error: GROUP_JID: invalid jid type: Not an instance of WID issue

* clean code

* Cleanup

* Fix for update chrome error

* ESLint fix

* :red_light: fix for RMDIR

* Update README.md

* Update README.md

* fix: getProfilePicUrl fix by victormga (#941)

* fix: MD presence available/unavailable (#942)

* delete session when appropriate & fix for SW

* ignore QR timeout errors

* Presence and ChatState updates working for MD+Non-MD

* shell uses new session storage

* lint fix

* support session.json-based auth for non-md

* md fix

* md fix

* fix shell clientId

* remove exclusive mocha test

* make linkPreview default to false

* remove ignored errors on getQuotedMessage

* fix: dont modify existing this.options.puppeteer object

* tests work with new dir auth

* remove exclusive test

* fixes and tests for group creation and participant functions

* remove unused function

* wip fix group settings functions

* isRegisteredUser && getNumberId hotFix (#955)

* isRegisteredUser && getNumberId hotFix

A fix for client.isRegisteredUser and client.getNumberId. Use for reference or if you are stuck with MD and NEEDS this function. Problably Whatsapp will break this in a couple weeks

* fix for non-md

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>

* Fix WA 2.2146.9 MD +  victormga branch (#991)

* qrcode now uses observers instead of timeout

* automatic auth/qrcode detection

* Fix WA 2.2146.9 MD

Got from github:victormga/whatsapp-web.js#multidevice maybe it's behind pedro branch

Co-authored-by: victormga <victor_mga@hotmail.com>

* fix

* fix*

* getnumberid to multidevice (#1027)

* getNumberId to main

 isRegisteredUser && getNumberId hotFix #955 To main

* Update Client.js

Co-authored-by: tuyuribr <45042245+tuyuribr@users.noreply.github.com>

* Update Client.js

* Message.raw() (#1005)

* Message.raw()

* i just noticed

* Update index.d.ts

* Update index.d.ts

* Update Message.js

* Get rid of sharp now!!!!!!!! (#1045)

* commit 1

* finally, gotten rid of sharp

* pckg.json

* service worker fix & disableMessage option

* typings

* Update example.js

* clear session system

* Update Client.js

* Update Client.js

* Fix accepting group private invite (#1094)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* [MD] Add getCommonGroups with specific user. (#1097)

* Add getCommonGroups with specific user.

* Fix

* Fix

* Fix

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Fix getCommonGroups. (#1122)

* Fix of Unexpected identifier async destroy() (#1123)

* Fix of Unexpected identifier async destroy()

* Fix made in #1107

* Temporary fix for "Sticker" module

* some really quick changes

* Update Injected.js

* Update Injected.js

* Update index.d.ts

* fix: getNumberId Solved (#1142)

* getNumberId Solved

* isRegisteredUser Solved

* formmated

* Apply suggestions from code review

* Update src/util/Injected.js

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>

* Fix: "Chrome user data dir was not found ..."

fixes the error caused by puppeteer.

* Update Client.js (#1154)

* fix: getNumberId and isRegisteredUser (#1159)

* fix: getNumberId and isRegisteredUser

* Apply suggestions from code review

Co-authored-by: Rajeh Taher <rajeh@reforward.dev>

* Update client.js

* Update Injected.js

* Update Client.js

* Update index.d.ts

* Update Client.js

* Update Client.js

* fix lint indentation

* fix auth_failure event for non-md, tests

* fix setting group subject

* fix finding Label module

* set remember-me after clearing localStorage

* fix: send messages to groups correctly on MD, use new ID format

* fix setting / getting contact status

* fix msg.getInfo, add message tests

* fix group settings functions

* fix set group description, handle errors in setSubject

* fix group invite functions

* fix leaving group

* bring back phone info for non-md users

* remove unused option, update typings

* add back jsdoc for qr event

* fix setting sticker metadata, clean up sticker functions

* rawData is a get only property

* fix and simplify getNumberId/isRegisteredUser

* fix getInviteInfo

* setDisplayName returns bool, not yet implemented for md

* fix: stream module (#1241)

* linkPreview has no effect on MD, return default to true

* fix: del linkPreview option on md

* cleanup, types and docs updates

* update readmes / test notes

* remove DS_Store

* DS_Store in gitignore

* test stability (timeouts/sleeps)

Co-authored-by: Rajeh Taher <rajeh@reforward.tk>
Co-authored-by: Gustavo B <52040719+Gugabit@users.noreply.github.com>
Co-authored-by: Maikel Ortega Hernández <maikeloh@gmail.com>
Co-authored-by: victormga <victor_mga@hotmail.com>
Co-authored-by: Pedro Lopez <pedroslopez@me.com>
Co-authored-by: tuyuribr <45042245+tuyuribr@users.noreply.github.com>
Co-authored-by: gon <68490103+nekiak@users.noreply.github.com>
Co-authored-by: Alon Schwartzblat <63599777+Schwartzblat@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Šebestíček <44745014+SebestikCZ@users.noreply.github.com>
Co-authored-by: Emmanuel Anaya Luna <38712443+KeruMx@users.noreply.github.com>
Co-authored-by: L337C0D3R <51872799+L337C0D3R@users.noreply.github.com>
Co-authored-by: Reni Delonzek <renidelonzek@gmail.com>
This commit is contained in:
Rajeh Taher
2022-02-27 14:51:08 -08:00
committed by GitHub
parent 8ef37b68ae
commit 0d55d40885
20 changed files with 1132 additions and 553 deletions

View File

@@ -1,4 +1,5 @@
const {expect} = require('chai');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const sinon = require('sinon');
const helper = require('./helper');
@@ -9,7 +10,11 @@ const MessageMedia = require('../src/structures/MessageMedia');
const Location = require('../src/structures/Location');
const { MessageTypes, WAState } = require('../src/util/Constants');
const expect = chai.expect;
chai.use(chaiAsPromised);
const remoteId = helper.remoteId;
const isMD = helper.isMD();
describe('Client', function() {
describe('Authentication', function() {
@@ -47,76 +52,6 @@ describe('Client', function() {
expect(disconnectedCallback.calledOnceWith('Max qrcode retries reached')).to.eql(true);
});
it('should fail auth if session is invalid', async function() {
this.timeout(40000);
const authFailCallback = sinon.spy();
const qrCallback = sinon.spy();
const readyCallback = sinon.spy();
const client = helper.createClient({
options: {
session: {
WABrowserId: 'invalid',
WASecretBundle: 'invalid',
WAToken1: 'invalid',
WAToken2: 'invalid'
},
authTimeoutMs: 10000,
restartOnAuthFail: false
}
});
client.on('qr', qrCallback);
client.on('auth_failure', authFailCallback);
client.on('ready', readyCallback);
client.initialize();
await helper.sleep(25000);
expect(authFailCallback.called).to.equal(true);
expect(authFailCallback.args[0][0]).to.equal('Unable to log in. Are the session details valid?');
expect(readyCallback.called).to.equal(false);
expect(qrCallback.called).to.equal(false);
await client.destroy();
});
it('can restart without a session if session was invalid and restartOnAuthFail=true', async function() {
this.timeout(40000);
const authFailCallback = sinon.spy();
const qrCallback = sinon.spy();
const client = helper.createClient({
options:{
session: {
WABrowserId: 'invalid',
WASecretBundle: 'invalid',
WAToken1: 'invalid',
WAToken2: 'invalid'
},
authTimeoutMs: 10000,
restartOnAuthFail: true
}
});
client.on('auth_failure', authFailCallback);
client.on('qr', qrCallback);
client.initialize();
await helper.sleep(35000);
expect(authFailCallback.called).to.equal(true);
expect(qrCallback.called).to.equal(true);
expect(qrCallback.args[0][0]).to.have.lengthOf(152);
await client.destroy();
});
it('should authenticate with existing session', async function() {
this.timeout(40000);
@@ -124,7 +59,10 @@ describe('Client', function() {
const qrCallback = sinon.spy();
const readyCallback = sinon.spy();
const client = helper.createClient({withSession: true});
const client = helper.createClient({
authenticated: true,
});
client.on('qr', qrCallback);
client.on('authenticated', authenticatedCallback);
client.on('ready', readyCallback);
@@ -132,59 +70,137 @@ describe('Client', function() {
await client.initialize();
expect(authenticatedCallback.called).to.equal(true);
const newSession = authenticatedCallback.args[0][0];
expect(newSession).to.have.key([
'WABrowserId',
'WASecretBundle',
'WAToken1',
'WAToken2'
]);
expect(authenticatedCallback.called).to.equal(true);
if(helper.isUsingDeprecatedSession()) {
const newSession = authenticatedCallback.args[0][0];
expect(newSession).to.have.key([
'WABrowserId',
'WASecretBundle',
'WAToken1',
'WAToken2'
]);
}
expect(readyCallback.called).to.equal(true);
expect(qrCallback.called).to.equal(false);
await client.destroy();
});
it('can take over if client was logged in somewhere else with takeoverOnConflict=true', async function() {
this.timeout(40000);
const readyCallback1 = sinon.spy();
const readyCallback2 = sinon.spy();
const disconnectedCallback1 = sinon.spy();
const disconnectedCallback2 = sinon.spy();
const client1 = helper.createClient({
withSession: true,
options: { takeoverOnConflict: true, takeoverTimeoutMs: 5000 }
});
const client2 = helper.createClient({withSession: true});
client1.on('ready', readyCallback1);
client2.on('ready', readyCallback2);
client1.on('disconnected', disconnectedCallback1);
client2.on('disconnected', disconnectedCallback2);
await client1.initialize();
expect(readyCallback1.called).to.equal(true);
expect(readyCallback2.called).to.equal(false);
expect(disconnectedCallback1.called).to.equal(false);
expect(disconnectedCallback2.called).to.equal(false);
await client2.initialize();
expect(readyCallback2.called).to.equal(true);
expect(disconnectedCallback1.called).to.equal(false);
expect(disconnectedCallback2.called).to.equal(false);
// wait for takeoverTimeoutMs to kick in
await helper.sleep(5200);
expect(disconnectedCallback1.called).to.equal(false);
expect(disconnectedCallback2.called).to.equal(true);
expect(disconnectedCallback2.calledWith(WAState.CONFLICT)).to.equal(true);
await client1.destroy();
});
describe('Non-MD only', function () {
if(!isMD) {
it('can take over if client was logged in somewhere else with takeoverOnConflict=true', async function() {
this.timeout(40000);
const readyCallback1 = sinon.spy();
const readyCallback2 = sinon.spy();
const disconnectedCallback1 = sinon.spy();
const disconnectedCallback2 = sinon.spy();
const client1 = helper.createClient({
authenticated: true,
options: { takeoverOnConflict: true, takeoverTimeoutMs: 5000 }
});
const client2 = helper.createClient({authenticated: true});
client1.on('ready', readyCallback1);
client2.on('ready', readyCallback2);
client1.on('disconnected', disconnectedCallback1);
client2.on('disconnected', disconnectedCallback2);
await client1.initialize();
expect(readyCallback1.called).to.equal(true);
expect(readyCallback2.called).to.equal(false);
expect(disconnectedCallback1.called).to.equal(false);
expect(disconnectedCallback2.called).to.equal(false);
await client2.initialize();
expect(readyCallback2.called).to.equal(true);
expect(disconnectedCallback1.called).to.equal(false);
expect(disconnectedCallback2.called).to.equal(false);
// wait for takeoverTimeoutMs to kick in
await helper.sleep(5200);
expect(disconnectedCallback1.called).to.equal(false);
expect(disconnectedCallback2.called).to.equal(true);
expect(disconnectedCallback2.calledWith(WAState.CONFLICT)).to.equal(true);
await client1.destroy();
});
it('should fail auth if session is invalid', async function() {
this.timeout(40000);
const authFailCallback = sinon.spy();
const qrCallback = sinon.spy();
const readyCallback = sinon.spy();
const client = helper.createClient({
options: {
session: {
WABrowserId: 'invalid',
WASecretBundle: 'invalid',
WAToken1: 'invalid',
WAToken2: 'invalid'
},
authTimeoutMs: 10000,
restartOnAuthFail: false,
useDeprecatedSessionAuth: true
}
});
client.on('qr', qrCallback);
client.on('auth_failure', authFailCallback);
client.on('ready', readyCallback);
client.initialize();
await helper.sleep(25000);
expect(authFailCallback.called).to.equal(true);
expect(authFailCallback.args[0][0]).to.equal('Unable to log in. Are the session details valid?');
expect(readyCallback.called).to.equal(false);
expect(qrCallback.called).to.equal(false);
await client.destroy();
});
it('can restart without a session if session was invalid and restartOnAuthFail=true', async function() {
this.timeout(40000);
const authFailCallback = sinon.spy();
const qrCallback = sinon.spy();
const client = helper.createClient({
options:{
session: {
WABrowserId: 'invalid',
WASecretBundle: 'invalid',
WAToken1: 'invalid',
WAToken2: 'invalid'
},
authTimeoutMs: 10000,
restartOnAuthFail: true,
useDeprecatedSessionAuth: true
}
});
client.on('auth_failure', authFailCallback);
client.on('qr', qrCallback);
client.initialize();
await helper.sleep(35000);
expect(authFailCallback.called).to.equal(true);
expect(qrCallback.called).to.equal(true);
expect(qrCallback.args[0][0]).to.have.lengthOf(152);
await client.destroy();
});
}
});
});
describe('Authenticated', function() {
@@ -192,7 +208,7 @@ describe('Client', function() {
before(async function() {
this.timeout(35000);
client = helper.createClient({withSession: true});
client = helper.createClient({authenticated: true});
await client.initialize();
});
@@ -221,27 +237,38 @@ describe('Client', function() {
'BlockContact',
'Call',
'Chat',
'ChatState',
'Cmd',
'Conn',
'Contact',
'DownloadManager',
'Features',
'GroupMetadata',
'GroupParticipants',
'GroupUtils',
'Invite',
'InviteInfo',
'JoinInviteV4',
'Label',
'MediaObject',
'MediaPrep',
'MediaTypes',
'MediaUpload',
'MessageInfo',
'Msg',
'MsgKey',
'OpaqueData',
'QueryOrder',
'QueryProduct',
'PresenceUtils',
'QueryExist',
'QueryProduct',
'QueryOrder',
'SendClear',
'SendDelete',
'SendMessage',
'SendSeen',
'StatusUtils',
'Sticker',
'UploadUtils',
'UserConstructor',
@@ -249,7 +276,9 @@ describe('Client', function() {
'Validators',
'Wap',
'WidFactory',
'genId'
'findCommonGroups',
'genId',
'getProfilePicFull',
];
const loadedModules = await client.pupPage.evaluate((expectedModules) => {
@@ -535,8 +564,6 @@ END:VCARD`;
describe('Search messages', function () {
it('can search for messages', async function () {
this.timeout(5000);
const m1 = await client.sendMessage(remoteId, 'I\'m searching for Super Mario Brothers');
const m2 = await client.sendMessage(remoteId, 'This also contains Mario');
const m3 = await client.sendMessage(remoteId, 'Nothing of interest here, just Luigi');
@@ -581,4 +608,4 @@ END:VCARD`;
});
});
});
});
});