whatsapp-web.js 1.26.0 → 1.26.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/example.js +8 -1
- package/index.d.ts +6 -1
- package/package.json +1 -1
- package/src/Client.js +233 -228
- package/src/structures/Chat.js +8 -0
- package/src/util/Injected/Store.js +1 -0
- package/src/util/InterfaceController.js +3 -3
- package/src/util/Puppeteer.js +23 -0
package/example.js
CHANGED
|
@@ -450,7 +450,7 @@ client.on('message', async msg => {
|
|
|
450
450
|
*/
|
|
451
451
|
const result = await msg.pin(60); // Will pin a message for 1 minute
|
|
452
452
|
console.log(result); // True if the operation completed successfully, false otherwise
|
|
453
|
-
}else if (msg.body === '!howManyConnections'){
|
|
453
|
+
} else if (msg.body === '!howManyConnections') {
|
|
454
454
|
/**
|
|
455
455
|
* Get user device count by ID
|
|
456
456
|
* Each WaWeb Connection counts as one device, and the phone (if exists) counts as one
|
|
@@ -458,6 +458,13 @@ client.on('message', async msg => {
|
|
|
458
458
|
*/
|
|
459
459
|
let deviceCount = await client.getContactDeviceCount(msg.from);
|
|
460
460
|
await msg.reply(`You have *${deviceCount}* devices connected`);
|
|
461
|
+
} else if (msg.body === '!syncHistory') {
|
|
462
|
+
const isSynced = await client.syncHistory(msg.from);
|
|
463
|
+
// Or through the Chat object:
|
|
464
|
+
// const chat = await client.getChatById(msg.from);
|
|
465
|
+
// const isSynced = await chat.syncHistory();
|
|
466
|
+
|
|
467
|
+
await msg.reply(isSynced ? 'Historical chat is syncing..' : 'There is no historical chat to sync.');
|
|
461
468
|
}
|
|
462
469
|
});
|
|
463
470
|
|
package/index.d.ts
CHANGED
|
@@ -183,7 +183,10 @@ declare namespace WAWebJS {
|
|
|
183
183
|
* So for a non-enterprise user with one WaWeb connection it should return "2"
|
|
184
184
|
* @param {string} contactId
|
|
185
185
|
*/
|
|
186
|
-
getContactDeviceCount(
|
|
186
|
+
getContactDeviceCount(userId: string): Promise<number>
|
|
187
|
+
|
|
188
|
+
/** Sync history conversation of the Chat */
|
|
189
|
+
syncHistory(chatId: string): Promise<boolean>
|
|
187
190
|
|
|
188
191
|
/** Changes and returns the archive state of the Chat */
|
|
189
192
|
unarchiveChat(chatId: string): Promise<boolean>
|
|
@@ -1440,6 +1443,8 @@ declare namespace WAWebJS {
|
|
|
1440
1443
|
getLabels: () => Promise<Label[]>,
|
|
1441
1444
|
/** Add or remove labels to this Chat */
|
|
1442
1445
|
changeLabels: (labelIds: Array<string | number>) => Promise<void>
|
|
1446
|
+
/** Sync history conversation of the Chat */
|
|
1447
|
+
syncHistory: () => Promise<boolean>
|
|
1443
1448
|
}
|
|
1444
1449
|
|
|
1445
1450
|
export interface MessageSearchOptions {
|
package/package.json
CHANGED
package/src/Client.js
CHANGED
|
@@ -17,6 +17,7 @@ const ContactFactory = require('./factories/ContactFactory');
|
|
|
17
17
|
const WebCacheFactory = require('./webCache/WebCacheFactory');
|
|
18
18
|
const { ClientInfo, Message, MessageMedia, Contact, Location, Poll, PollVote, GroupNotification, Label, Call, Buttons, List, Reaction } = require('./structures');
|
|
19
19
|
const NoAuth = require('./authStrategies/NoAuth');
|
|
20
|
+
const {exposeFunctionIfAbsent} = require('./util/Puppeteer');
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Starting point for interacting with the WhatsApp Web API
|
|
@@ -90,9 +91,8 @@ class Client extends EventEmitter {
|
|
|
90
91
|
/**
|
|
91
92
|
* Injection logic
|
|
92
93
|
* Private function
|
|
93
|
-
* @property {boolean} reinject is this a reinject?
|
|
94
94
|
*/
|
|
95
|
-
async inject(
|
|
95
|
+
async inject() {
|
|
96
96
|
await this.pupPage.waitForFunction('window.Debug?.VERSION != undefined', {timeout: this.options.authTimeoutMs});
|
|
97
97
|
|
|
98
98
|
const version = await this.getWWebVersion();
|
|
@@ -142,26 +142,21 @@ class Client extends EventEmitter {
|
|
|
142
142
|
|
|
143
143
|
// Register qr events
|
|
144
144
|
let qrRetries = 0;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
qrRetries++;
|
|
158
|
-
if (qrRetries > this.options.qrMaxRetries) {
|
|
159
|
-
this.emit(Events.DISCONNECTED, 'Max qrcode retries reached');
|
|
160
|
-
await this.destroy();
|
|
161
|
-
}
|
|
145
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onQRChangedEvent', async (qr) => {
|
|
146
|
+
/**
|
|
147
|
+
* Emitted when a QR code is received
|
|
148
|
+
* @event Client#qr
|
|
149
|
+
* @param {string} qr QR Code
|
|
150
|
+
*/
|
|
151
|
+
this.emit(Events.QR_RECEIVED, qr);
|
|
152
|
+
if (this.options.qrMaxRetries > 0) {
|
|
153
|
+
qrRetries++;
|
|
154
|
+
if (qrRetries > this.options.qrMaxRetries) {
|
|
155
|
+
this.emit(Events.DISCONNECTED, 'Max qrcode retries reached');
|
|
156
|
+
await this.destroy();
|
|
162
157
|
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
165
160
|
|
|
166
161
|
|
|
167
162
|
await this.pupPage.evaluate(async () => {
|
|
@@ -178,86 +173,78 @@ class Client extends EventEmitter {
|
|
|
178
173
|
});
|
|
179
174
|
}
|
|
180
175
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
});
|
|
176
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onAuthAppStateChangedEvent', async (state) => {
|
|
177
|
+
if (state == 'UNPAIRED_IDLE') {
|
|
178
|
+
// refresh qr code
|
|
179
|
+
window.Store.Cmd.refreshQR();
|
|
180
|
+
}
|
|
181
|
+
});
|
|
188
182
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
183
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onAppStateHasSyncedEvent', async () => {
|
|
184
|
+
const authEventPayload = await this.authStrategy.getAuthEventPayload();
|
|
185
|
+
/**
|
|
192
186
|
* Emitted when authentication is successful
|
|
193
187
|
* @event Client#authenticated
|
|
194
188
|
*/
|
|
195
|
-
|
|
189
|
+
this.emit(Events.AUTHENTICATED, authEventPayload);
|
|
196
190
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
191
|
+
const injected = await this.pupPage.evaluate(async () => {
|
|
192
|
+
return typeof window.Store !== 'undefined' && typeof window.WWebJS !== 'undefined';
|
|
193
|
+
});
|
|
200
194
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
195
|
+
if (!injected) {
|
|
196
|
+
if (this.options.webVersionCache.type === 'local' && this.currentIndexHtml) {
|
|
197
|
+
const { type: webCacheType, ...webCacheOptions } = this.options.webVersionCache;
|
|
198
|
+
const webCache = WebCacheFactory.createWebCache(webCacheType, webCacheOptions);
|
|
205
199
|
|
|
206
|
-
|
|
207
|
-
|
|
200
|
+
await webCache.persist(this.currentIndexHtml, version);
|
|
201
|
+
}
|
|
208
202
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
203
|
+
if (isCometOrAbove) {
|
|
204
|
+
await this.pupPage.evaluate(ExposeStore);
|
|
205
|
+
} else {
|
|
206
|
+
// make sure all modules are ready before injection
|
|
207
|
+
// 2 second delay after authentication makes sense and does not need to be made dyanmic or removed
|
|
208
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
209
|
+
await this.pupPage.evaluate(ExposeLegacyStore);
|
|
210
|
+
}
|
|
217
211
|
|
|
218
|
-
|
|
219
|
-
|
|
212
|
+
// Check window.Store Injection
|
|
213
|
+
await this.pupPage.waitForFunction('window.Store != undefined');
|
|
220
214
|
|
|
221
|
-
|
|
215
|
+
/**
|
|
222
216
|
* Current connection information
|
|
223
217
|
* @type {ClientInfo}
|
|
224
218
|
*/
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
219
|
+
this.info = new ClientInfo(this, await this.pupPage.evaluate(() => {
|
|
220
|
+
return { ...window.Store.Conn.serialize(), wid: window.Store.User.getMeUser() };
|
|
221
|
+
}));
|
|
228
222
|
|
|
229
|
-
|
|
223
|
+
this.interface = new InterfaceController(this);
|
|
230
224
|
|
|
231
|
-
|
|
232
|
-
|
|
225
|
+
//Load util functions (serializers, helper functions)
|
|
226
|
+
await this.pupPage.evaluate(LoadUtils);
|
|
233
227
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
/**
|
|
228
|
+
await this.attachEventListeners();
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
238
231
|
* Emitted when the client has initialized and is ready to receive messages.
|
|
239
232
|
* @event Client#ready
|
|
240
233
|
*/
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
234
|
+
this.emit(Events.READY);
|
|
235
|
+
this.authStrategy.afterAuthReady();
|
|
236
|
+
});
|
|
237
|
+
let lastPercent = null;
|
|
238
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onOfflineProgressUpdateEvent', async (percent) => {
|
|
239
|
+
if (lastPercent !== percent) {
|
|
240
|
+
lastPercent = percent;
|
|
241
|
+
this.emit(Events.LOADING_SCREEN, percent, 'WhatsApp'); // Message is hardcoded as "WhatsApp" for now
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onLogoutEvent', async () => {
|
|
245
|
+
this.lastLoggedOut = true;
|
|
246
|
+
await this.pupPage.waitForNavigation({waitUntil: 'load', timeout: 5000}).catch((_) => _);
|
|
254
247
|
});
|
|
255
|
-
if (!logoutCatchInjected) {
|
|
256
|
-
await this.pupPage.exposeFunction('onLogoutEvent', async () => {
|
|
257
|
-
this.lastLoggedOut = true;
|
|
258
|
-
await this.pupPage.waitForNavigation({waitUntil: 'load', timeout: 5000}).catch((_) => _);
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
248
|
await this.pupPage.evaluate(() => {
|
|
262
249
|
window.AuthStore.AppState.on('change:state', (_AppState, state) => { window.onAuthAppStateChangedEvent(state); });
|
|
263
250
|
window.AuthStore.AppState.on('change:hasSynced', () => { window.onAppStateHasSyncedEvent(); });
|
|
@@ -349,7 +336,7 @@ class Client extends EventEmitter {
|
|
|
349
336
|
await this.authStrategy.afterBrowserInitialized();
|
|
350
337
|
this.lastLoggedOut = false;
|
|
351
338
|
}
|
|
352
|
-
await this.inject(
|
|
339
|
+
await this.inject();
|
|
353
340
|
});
|
|
354
341
|
}
|
|
355
342
|
|
|
@@ -372,34 +359,33 @@ class Client extends EventEmitter {
|
|
|
372
359
|
* Private function
|
|
373
360
|
* @property {boolean} reinject is this a reinject?
|
|
374
361
|
*/
|
|
375
|
-
async attachEventListeners(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
/**
|
|
362
|
+
async attachEventListeners() {
|
|
363
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onAddMessageEvent', msg => {
|
|
364
|
+
if (msg.type === 'gp2') {
|
|
365
|
+
const notification = new GroupNotification(this, msg);
|
|
366
|
+
if (['add', 'invite', 'linked_group_join'].includes(msg.subtype)) {
|
|
367
|
+
/**
|
|
382
368
|
* Emitted when a user joins the chat via invite link or is added by an admin.
|
|
383
369
|
* @event Client#group_join
|
|
384
370
|
* @param {GroupNotification} notification GroupNotification with more information about the action
|
|
385
371
|
*/
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
372
|
+
this.emit(Events.GROUP_JOIN, notification);
|
|
373
|
+
} else if (msg.subtype === 'remove' || msg.subtype === 'leave') {
|
|
374
|
+
/**
|
|
389
375
|
* Emitted when a user leaves the chat or is removed by an admin.
|
|
390
376
|
* @event Client#group_leave
|
|
391
377
|
* @param {GroupNotification} notification GroupNotification with more information about the action
|
|
392
378
|
*/
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
379
|
+
this.emit(Events.GROUP_LEAVE, notification);
|
|
380
|
+
} else if (msg.subtype === 'promote' || msg.subtype === 'demote') {
|
|
381
|
+
/**
|
|
396
382
|
* Emitted when a current user is promoted to an admin or demoted to a regular user.
|
|
397
383
|
* @event Client#group_admin_changed
|
|
398
384
|
* @param {GroupNotification} notification GroupNotification with more information about the action
|
|
399
385
|
*/
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
386
|
+
this.emit(Events.GROUP_ADMIN_CHANGED, notification);
|
|
387
|
+
} else if (msg.subtype === 'membership_approval_request') {
|
|
388
|
+
/**
|
|
403
389
|
* Emitted when some user requested to join the group
|
|
404
390
|
* that has the membership approval mode turned on
|
|
405
391
|
* @event Client#group_membership_request
|
|
@@ -408,86 +394,86 @@ class Client extends EventEmitter {
|
|
|
408
394
|
* @param {string} notification.author The user ID that made a request
|
|
409
395
|
* @param {number} notification.timestamp The timestamp the request was made at
|
|
410
396
|
*/
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
397
|
+
this.emit(Events.GROUP_MEMBERSHIP_REQUEST, notification);
|
|
398
|
+
} else {
|
|
399
|
+
/**
|
|
414
400
|
* Emitted when group settings are updated, such as subject, description or picture.
|
|
415
401
|
* @event Client#group_update
|
|
416
402
|
* @param {GroupNotification} notification GroupNotification with more information about the action
|
|
417
403
|
*/
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
return;
|
|
404
|
+
this.emit(Events.GROUP_UPDATE, notification);
|
|
421
405
|
}
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
422
408
|
|
|
423
|
-
|
|
409
|
+
const message = new Message(this, msg);
|
|
424
410
|
|
|
425
|
-
|
|
411
|
+
/**
|
|
426
412
|
* Emitted when a new message is created, which may include the current user's own messages.
|
|
427
413
|
* @event Client#message_create
|
|
428
414
|
* @param {Message} message The message that was created
|
|
429
415
|
*/
|
|
430
|
-
|
|
416
|
+
this.emit(Events.MESSAGE_CREATE, message);
|
|
431
417
|
|
|
432
|
-
|
|
418
|
+
if (msg.id.fromMe) return;
|
|
433
419
|
|
|
434
|
-
|
|
420
|
+
/**
|
|
435
421
|
* Emitted when a new message is received.
|
|
436
422
|
* @event Client#message
|
|
437
423
|
* @param {Message} message The message that was received
|
|
438
424
|
*/
|
|
439
|
-
|
|
440
|
-
|
|
425
|
+
this.emit(Events.MESSAGE_RECEIVED, message);
|
|
426
|
+
});
|
|
441
427
|
|
|
442
|
-
|
|
428
|
+
let last_message;
|
|
443
429
|
|
|
444
|
-
|
|
430
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onChangeMessageTypeEvent', (msg) => {
|
|
445
431
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
432
|
+
if (msg.type === 'revoked') {
|
|
433
|
+
const message = new Message(this, msg);
|
|
434
|
+
let revoked_msg;
|
|
435
|
+
if (last_message && msg.id.id === last_message.id.id) {
|
|
436
|
+
revoked_msg = new Message(this, last_message);
|
|
437
|
+
}
|
|
452
438
|
|
|
453
|
-
|
|
439
|
+
/**
|
|
454
440
|
* Emitted when a message is deleted for everyone in the chat.
|
|
455
441
|
* @event Client#message_revoke_everyone
|
|
456
442
|
* @param {Message} message The message that was revoked, in its current state. It will not contain the original message's data.
|
|
457
443
|
* @param {?Message} revoked_msg The message that was revoked, before it was revoked. It will contain the message's original data.
|
|
458
444
|
* Note that due to the way this data is captured, it may be possible that this param will be undefined.
|
|
459
445
|
*/
|
|
460
|
-
|
|
461
|
-
|
|
446
|
+
this.emit(Events.MESSAGE_REVOKED_EVERYONE, message, revoked_msg);
|
|
447
|
+
}
|
|
462
448
|
|
|
463
|
-
|
|
449
|
+
});
|
|
464
450
|
|
|
465
|
-
|
|
451
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onChangeMessageEvent', (msg) => {
|
|
466
452
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
453
|
+
if (msg.type !== 'revoked') {
|
|
454
|
+
last_message = msg;
|
|
455
|
+
}
|
|
470
456
|
|
|
471
|
-
|
|
457
|
+
/**
|
|
472
458
|
* The event notification that is received when one of
|
|
473
459
|
* the group participants changes their phone number.
|
|
474
460
|
*/
|
|
475
|
-
|
|
461
|
+
const isParticipant = msg.type === 'gp2' && msg.subtype === 'modify';
|
|
476
462
|
|
|
477
|
-
|
|
463
|
+
/**
|
|
478
464
|
* The event notification that is received when one of
|
|
479
465
|
* the contacts changes their phone number.
|
|
480
466
|
*/
|
|
481
|
-
|
|
467
|
+
const isContact = msg.type === 'notification_template' && msg.subtype === 'change_number';
|
|
482
468
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
469
|
+
if (isParticipant || isContact) {
|
|
470
|
+
/** @type {GroupNotification} object does not provide enough information about this event, so a @type {Message} object is used. */
|
|
471
|
+
const message = new Message(this, msg);
|
|
486
472
|
|
|
487
|
-
|
|
488
|
-
|
|
473
|
+
const newId = isParticipant ? msg.recipients[0] : msg.to;
|
|
474
|
+
const oldId = isParticipant ? msg.author : msg.templateParams.find(id => id !== newId);
|
|
489
475
|
|
|
490
|
-
|
|
476
|
+
/**
|
|
491
477
|
* Emitted when a contact or a group participant changes their phone number.
|
|
492
478
|
* @event Client#contact_changed
|
|
493
479
|
* @param {Message} message Message with more information about the event.
|
|
@@ -496,98 +482,98 @@ class Client extends EventEmitter {
|
|
|
496
482
|
* @param {String} newId The user's new id after the change.
|
|
497
483
|
* @param {Boolean} isContact Indicates if a contact or a group participant changed their phone number.
|
|
498
484
|
*/
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
485
|
+
this.emit(Events.CONTACT_CHANGED, message, oldId, newId, isContact);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
502
488
|
|
|
503
|
-
|
|
489
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onRemoveMessageEvent', (msg) => {
|
|
504
490
|
|
|
505
|
-
|
|
491
|
+
if (!msg.isNewMsg) return;
|
|
506
492
|
|
|
507
|
-
|
|
493
|
+
const message = new Message(this, msg);
|
|
508
494
|
|
|
509
|
-
|
|
495
|
+
/**
|
|
510
496
|
* Emitted when a message is deleted by the current user.
|
|
511
497
|
* @event Client#message_revoke_me
|
|
512
498
|
* @param {Message} message The message that was revoked
|
|
513
499
|
*/
|
|
514
|
-
|
|
500
|
+
this.emit(Events.MESSAGE_REVOKED_ME, message);
|
|
515
501
|
|
|
516
|
-
|
|
502
|
+
});
|
|
517
503
|
|
|
518
|
-
|
|
504
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onMessageAckEvent', (msg, ack) => {
|
|
519
505
|
|
|
520
|
-
|
|
506
|
+
const message = new Message(this, msg);
|
|
521
507
|
|
|
522
|
-
|
|
508
|
+
/**
|
|
523
509
|
* Emitted when an ack event occurrs on message type.
|
|
524
510
|
* @event Client#message_ack
|
|
525
511
|
* @param {Message} message The message that was affected
|
|
526
512
|
* @param {MessageAck} ack The new ACK value
|
|
527
513
|
*/
|
|
528
|
-
|
|
514
|
+
this.emit(Events.MESSAGE_ACK, message, ack);
|
|
529
515
|
|
|
530
|
-
|
|
516
|
+
});
|
|
531
517
|
|
|
532
|
-
|
|
533
|
-
|
|
518
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onChatUnreadCountEvent', async (data) =>{
|
|
519
|
+
const chat = await this.getChatById(data.id);
|
|
534
520
|
|
|
535
|
-
|
|
521
|
+
/**
|
|
536
522
|
* Emitted when the chat unread count changes
|
|
537
523
|
*/
|
|
538
|
-
|
|
539
|
-
|
|
524
|
+
this.emit(Events.UNREAD_COUNT, chat);
|
|
525
|
+
});
|
|
540
526
|
|
|
541
|
-
|
|
527
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onMessageMediaUploadedEvent', (msg) => {
|
|
542
528
|
|
|
543
|
-
|
|
529
|
+
const message = new Message(this, msg);
|
|
544
530
|
|
|
545
|
-
|
|
531
|
+
/**
|
|
546
532
|
* Emitted when media has been uploaded for a message sent by the client.
|
|
547
533
|
* @event Client#media_uploaded
|
|
548
534
|
* @param {Message} message The message with media that was uploaded
|
|
549
535
|
*/
|
|
550
|
-
|
|
551
|
-
|
|
536
|
+
this.emit(Events.MEDIA_UPLOADED, message);
|
|
537
|
+
});
|
|
552
538
|
|
|
553
|
-
|
|
554
|
-
|
|
539
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onAppStateChangedEvent', async (state) => {
|
|
540
|
+
/**
|
|
555
541
|
* Emitted when the connection state changes
|
|
556
542
|
* @event Client#change_state
|
|
557
543
|
* @param {WAState} state the new connection state
|
|
558
544
|
*/
|
|
559
|
-
|
|
545
|
+
this.emit(Events.STATE_CHANGED, state);
|
|
560
546
|
|
|
561
|
-
|
|
547
|
+
const ACCEPTED_STATES = [WAState.CONNECTED, WAState.OPENING, WAState.PAIRING, WAState.TIMEOUT];
|
|
562
548
|
|
|
563
|
-
|
|
564
|
-
|
|
549
|
+
if (this.options.takeoverOnConflict) {
|
|
550
|
+
ACCEPTED_STATES.push(WAState.CONFLICT);
|
|
565
551
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
552
|
+
if (state === WAState.CONFLICT) {
|
|
553
|
+
setTimeout(() => {
|
|
554
|
+
this.pupPage.evaluate(() => window.Store.AppState.takeover());
|
|
555
|
+
}, this.options.takeoverTimeoutMs);
|
|
571
556
|
}
|
|
557
|
+
}
|
|
572
558
|
|
|
573
|
-
|
|
574
|
-
|
|
559
|
+
if (!ACCEPTED_STATES.includes(state)) {
|
|
560
|
+
/**
|
|
575
561
|
* Emitted when the client has been disconnected
|
|
576
562
|
* @event Client#disconnected
|
|
577
563
|
* @param {WAState|"LOGOUT"} reason reason that caused the disconnect
|
|
578
564
|
*/
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
565
|
+
await this.authStrategy.disconnect();
|
|
566
|
+
this.emit(Events.DISCONNECTED, state);
|
|
567
|
+
this.destroy();
|
|
568
|
+
}
|
|
569
|
+
});
|
|
584
570
|
|
|
585
|
-
|
|
586
|
-
|
|
571
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onBatteryStateChangedEvent', (state) => {
|
|
572
|
+
const { battery, plugged } = state;
|
|
587
573
|
|
|
588
|
-
|
|
574
|
+
if (battery === undefined) return;
|
|
589
575
|
|
|
590
|
-
|
|
576
|
+
/**
|
|
591
577
|
* Emitted when the battery percentage for the attached device changes. Will not be sent if using multi-device.
|
|
592
578
|
* @event Client#change_battery
|
|
593
579
|
* @param {object} batteryInfo
|
|
@@ -595,11 +581,11 @@ class Client extends EventEmitter {
|
|
|
595
581
|
* @param {boolean} batteryInfo.plugged - Indicates if the phone is plugged in (true) or not (false)
|
|
596
582
|
* @deprecated
|
|
597
583
|
*/
|
|
598
|
-
|
|
599
|
-
|
|
584
|
+
this.emit(Events.BATTERY_CHANGED, { battery, plugged });
|
|
585
|
+
});
|
|
600
586
|
|
|
601
|
-
|
|
602
|
-
|
|
587
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onIncomingCall', (call) => {
|
|
588
|
+
/**
|
|
603
589
|
* Emitted when a call is received
|
|
604
590
|
* @event Client#incoming_call
|
|
605
591
|
* @param {object} call
|
|
@@ -612,13 +598,13 @@ class Client extends EventEmitter {
|
|
|
612
598
|
* @param {boolean} call.webClientShouldHandle - If Waweb should handle
|
|
613
599
|
* @param {object} call.participants - Participants
|
|
614
600
|
*/
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
601
|
+
const cll = new Call(this, call);
|
|
602
|
+
this.emit(Events.INCOMING_CALL, cll);
|
|
603
|
+
});
|
|
618
604
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
605
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onReaction', (reactions) => {
|
|
606
|
+
for (const reaction of reactions) {
|
|
607
|
+
/**
|
|
622
608
|
* Emitted when a reaction is sent, received, updated or removed
|
|
623
609
|
* @event Client#message_reaction
|
|
624
610
|
* @param {object} reaction
|
|
@@ -633,61 +619,60 @@ class Client extends EventEmitter {
|
|
|
633
619
|
* @param {?number} reaction.ack - Ack
|
|
634
620
|
*/
|
|
635
621
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
622
|
+
this.emit(Events.MESSAGE_REACTION, new Reaction(this, reaction));
|
|
623
|
+
}
|
|
624
|
+
});
|
|
639
625
|
|
|
640
|
-
|
|
641
|
-
|
|
626
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onRemoveChatEvent', async (chat) => {
|
|
627
|
+
const _chat = await this.getChatById(chat.id);
|
|
642
628
|
|
|
643
|
-
|
|
629
|
+
/**
|
|
644
630
|
* Emitted when a chat is removed
|
|
645
631
|
* @event Client#chat_removed
|
|
646
632
|
* @param {Chat} chat
|
|
647
633
|
*/
|
|
648
|
-
|
|
649
|
-
|
|
634
|
+
this.emit(Events.CHAT_REMOVED, _chat);
|
|
635
|
+
});
|
|
650
636
|
|
|
651
|
-
|
|
652
|
-
|
|
637
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onArchiveChatEvent', async (chat, currState, prevState) => {
|
|
638
|
+
const _chat = await this.getChatById(chat.id);
|
|
653
639
|
|
|
654
|
-
|
|
640
|
+
/**
|
|
655
641
|
* Emitted when a chat is archived/unarchived
|
|
656
642
|
* @event Client#chat_archived
|
|
657
643
|
* @param {Chat} chat
|
|
658
644
|
* @param {boolean} currState
|
|
659
645
|
* @param {boolean} prevState
|
|
660
646
|
*/
|
|
661
|
-
|
|
662
|
-
|
|
647
|
+
this.emit(Events.CHAT_ARCHIVED, _chat, currState, prevState);
|
|
648
|
+
});
|
|
663
649
|
|
|
664
|
-
|
|
650
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onEditMessageEvent', (msg, newBody, prevBody) => {
|
|
665
651
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
652
|
+
if(msg.type === 'revoked'){
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
670
656
|
* Emitted when messages are edited
|
|
671
657
|
* @event Client#message_edit
|
|
672
658
|
* @param {Message} message
|
|
673
659
|
* @param {string} newBody
|
|
674
660
|
* @param {string} prevBody
|
|
675
661
|
*/
|
|
676
|
-
|
|
677
|
-
|
|
662
|
+
this.emit(Events.MESSAGE_EDIT, new Message(this, msg), newBody, prevBody);
|
|
663
|
+
});
|
|
678
664
|
|
|
679
|
-
|
|
665
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onAddMessageCiphertextEvent', msg => {
|
|
680
666
|
|
|
681
|
-
|
|
667
|
+
/**
|
|
682
668
|
* Emitted when messages are edited
|
|
683
669
|
* @event Client#message_ciphertext
|
|
684
670
|
* @param {Message} message
|
|
685
671
|
*/
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
}
|
|
672
|
+
this.emit(Events.MESSAGE_CIPHERTEXT, new Message(this, msg));
|
|
673
|
+
});
|
|
689
674
|
|
|
690
|
-
await this.pupPage
|
|
675
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onPollVoteEvent', (vote) => {
|
|
691
676
|
const _vote = new PollVote(this, vote);
|
|
692
677
|
/**
|
|
693
678
|
* Emitted when some poll option is selected or deselected,
|
|
@@ -1729,16 +1714,36 @@ class Client extends EventEmitter {
|
|
|
1729
1714
|
* Get user device count by ID
|
|
1730
1715
|
* Each WaWeb Connection counts as one device, and the phone (if exists) counts as one
|
|
1731
1716
|
* So for a non-enterprise user with one WaWeb connection it should return "2"
|
|
1732
|
-
* @param {string}
|
|
1733
|
-
* @returns {number}
|
|
1717
|
+
* @param {string} userId
|
|
1718
|
+
* @returns {Promise<number>}
|
|
1734
1719
|
*/
|
|
1735
|
-
async getContactDeviceCount(
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1720
|
+
async getContactDeviceCount(userId) {
|
|
1721
|
+
return await this.pupPage.evaluate(async (userId) => {
|
|
1722
|
+
const devices = await window.Store.DeviceList.getDeviceIds([window.Store.WidFactory.createWid(userId)]);
|
|
1723
|
+
if (devices && devices.length && devices[0] != null && typeof devices[0].devices == 'object') {
|
|
1724
|
+
return devices[0].devices.length;
|
|
1725
|
+
}
|
|
1726
|
+
return 0;
|
|
1727
|
+
}, userId);
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
/**
|
|
1731
|
+
* Sync chat history conversation
|
|
1732
|
+
* @param {string} chatId
|
|
1733
|
+
* @return {Promise<boolean>} True if operation completed successfully, false otherwise.
|
|
1734
|
+
*/
|
|
1735
|
+
async syncHistory(chatId) {
|
|
1736
|
+
return await this.pupPage.evaluate(async (chatId) => {
|
|
1737
|
+
const chat = await window.WWebJS.getChat(chatId);
|
|
1738
|
+
if (chat.endOfHistoryTransferType === 0) {
|
|
1739
|
+
await window.Store.HistorySync.sendPeerDataOperationRequest(3, {
|
|
1740
|
+
chatId: chat.id
|
|
1741
|
+
});
|
|
1742
|
+
return true;
|
|
1743
|
+
}
|
|
1744
|
+
return false;
|
|
1745
|
+
}, chatId);
|
|
1741
1746
|
}
|
|
1742
1747
|
}
|
|
1743
1748
|
|
|
1744
|
-
module.exports = Client;
|
|
1749
|
+
module.exports = Client;
|
package/src/structures/Chat.js
CHANGED
|
@@ -270,6 +270,14 @@ class Chat extends Base {
|
|
|
270
270
|
async changeLabels(labelIds) {
|
|
271
271
|
return this.client.addOrRemoveLabels(labelIds, [this.id._serialized]);
|
|
272
272
|
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Sync chat history conversation
|
|
276
|
+
* @return {Promise<boolean>} True if operation completed successfully, false otherwise.
|
|
277
|
+
*/
|
|
278
|
+
async syncHistory() {
|
|
279
|
+
return this.client.syncHistory(this.id._serialized);
|
|
280
|
+
}
|
|
273
281
|
}
|
|
274
282
|
|
|
275
283
|
module.exports = Chat;
|
|
@@ -98,6 +98,7 @@ exports.ExposeStore = () => {
|
|
|
98
98
|
window.Store.BotSecret = window.require('WAWebBotMessageSecret');
|
|
99
99
|
window.Store.BotProfiles = window.require('WAWebBotProfileCollection');
|
|
100
100
|
window.Store.DeviceList = window.require('WAWebApiDeviceList');
|
|
101
|
+
window.Store.HistorySync = window.require('WAWebSendNonMessageDataRequest');
|
|
101
102
|
if (window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.1014111620'))
|
|
102
103
|
window.Store.AddonReactionTable = window.require('WAWebAddonReactionTableMode').reactionTableMode;
|
|
103
104
|
|
|
@@ -15,9 +15,9 @@ class InterfaceController {
|
|
|
15
15
|
*/
|
|
16
16
|
async openChatWindow(chatId) {
|
|
17
17
|
await this.pupPage.evaluate(async chatId => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
await window.Store.Cmd.
|
|
18
|
+
const chatWid = window.Store.WidFactory.createWid(chatId);
|
|
19
|
+
const chat = window.Store.Chat.get(chatWid) || await window.Store.Chat.find(chatWid);
|
|
20
|
+
await window.Store.Cmd.openChatBottom(chat);
|
|
21
21
|
}, chatId);
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expose a function to the page if it does not exist
|
|
3
|
+
*
|
|
4
|
+
* NOTE:
|
|
5
|
+
* Rewrite it to 'upsertFunction' after updating Puppeteer to 20.6 or higher
|
|
6
|
+
* using page.removeExposedFunction
|
|
7
|
+
* https://pptr.dev/api/puppeteer.page.removeExposedFunction
|
|
8
|
+
*
|
|
9
|
+
* @param {import(puppeteer).Page} page
|
|
10
|
+
* @param {string} name
|
|
11
|
+
* @param {Function} fn
|
|
12
|
+
*/
|
|
13
|
+
async function exposeFunctionIfAbsent(page, name, fn) {
|
|
14
|
+
const exist = await page.evaluate((name) => {
|
|
15
|
+
return !!window[name];
|
|
16
|
+
}, name);
|
|
17
|
+
if (exist) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
await page.exposeFunction(name, fn);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = {exposeFunctionIfAbsent};
|