whatsapp-web.js 1.20.0 → 1.21.1-alpha.0
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/README.md +1 -1
- package/example.js +39 -12
- package/index.d.ts +59 -0
- package/package.json +1 -1
- package/src/Client.js +115 -11
- package/src/structures/Chat.js +9 -0
- package/src/structures/Contact.js +3 -1
- package/src/structures/Message.js +49 -2
- package/src/util/Constants.js +5 -0
- package/src/util/Injected.js +134 -12
- package/src/webCache/LocalWebCache.js +43 -0
- package/src/webCache/RemoteWebCache.js +40 -0
- package/src/webCache/WebCache.js +14 -0
- package/src/webCache/WebCacheFactory.js +20 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[](https://www.npmjs.com/package/whatsapp-web.js) [](https://depfu.com/github/pedroslopez/whatsapp-web.js?project_id=9765) ](https://www.npmjs.com/package/whatsapp-web.js) [](https://depfu.com/github/pedroslopez/whatsapp-web.js?project_id=9765)  [](https://discord.gg/H7DqQs4)
|
|
2
2
|
|
|
3
3
|
# whatsapp-web.js
|
|
4
4
|
A WhatsApp API client that connects through the WhatsApp Web browser app
|
package/example.js
CHANGED
|
@@ -140,6 +140,12 @@ client.on('message', async msg => {
|
|
|
140
140
|
const attachmentData = await quotedMsg.downloadMedia();
|
|
141
141
|
client.sendMessage(msg.from, attachmentData, { caption: 'Here\'s your requested media.' });
|
|
142
142
|
}
|
|
143
|
+
} else if (msg.body === '!isviewonce' && msg.hasQuotedMsg) {
|
|
144
|
+
const quotedMsg = await msg.getQuotedMessage();
|
|
145
|
+
if (quotedMsg.hasMedia) {
|
|
146
|
+
const media = await quotedMsg.downloadMedia();
|
|
147
|
+
await client.sendMessage(msg.from, media, { isViewOnce: true });
|
|
148
|
+
}
|
|
143
149
|
} else if (msg.body === '!location') {
|
|
144
150
|
msg.reply(new Location(37.422, -122.084, 'Googleplex\nGoogle Headquarters'));
|
|
145
151
|
} else if (msg.location) {
|
|
@@ -201,6 +207,27 @@ client.on('message', async msg => {
|
|
|
201
207
|
client.sendMessage(msg.from, list);
|
|
202
208
|
} else if (msg.body === '!reaction') {
|
|
203
209
|
msg.react('👍');
|
|
210
|
+
} else if (msg.body === '!edit') {
|
|
211
|
+
if (msg.hasQuotedMsg) {
|
|
212
|
+
const quotedMsg = await msg.getQuotedMessage();
|
|
213
|
+
if (quotedMsg.fromMe) {
|
|
214
|
+
quotedMsg.edit(msg.body.replace('!edit', ''));
|
|
215
|
+
} else {
|
|
216
|
+
msg.reply('I can only edit my own messages');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} else if (msg.body === '!updatelabels') {
|
|
220
|
+
const chat = await msg.getChat();
|
|
221
|
+
await chat.changeLabels([0, 1]);
|
|
222
|
+
} else if (msg.body === '!addlabels') {
|
|
223
|
+
const chat = await msg.getChat();
|
|
224
|
+
let labels = (await chat.getLabels()).map(l => l.id);
|
|
225
|
+
labels.push('0');
|
|
226
|
+
labels.push('1');
|
|
227
|
+
await chat.changeLabels(labels);
|
|
228
|
+
} else if (msg.body === '!removelabels') {
|
|
229
|
+
const chat = await msg.getChat();
|
|
230
|
+
await chat.changeLabels([]);
|
|
204
231
|
}
|
|
205
232
|
});
|
|
206
233
|
|
|
@@ -286,28 +313,28 @@ client.on('contact_changed', async (message, oldId, newId, isContact) => {
|
|
|
286
313
|
`Their new phone number is ${newId.slice(0, -5)}.\n`);
|
|
287
314
|
|
|
288
315
|
/**
|
|
289
|
-
* Information about the
|
|
316
|
+
* Information about the @param {message}:
|
|
290
317
|
*
|
|
291
318
|
* 1. If a notification was emitted due to a group participant changing their phone number:
|
|
292
|
-
*
|
|
293
|
-
*
|
|
319
|
+
* @param {message.author} is a participant's id before the change.
|
|
320
|
+
* @param {message.recipients[0]} is a participant's id after the change (a new one).
|
|
294
321
|
*
|
|
295
322
|
* 1.1 If the contact who changed their number WAS in the current user's contact list at the time of the change:
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
* Also the
|
|
323
|
+
* @param {message.to} is a group chat id the event was emitted in.
|
|
324
|
+
* @param {message.from} is a current user's id that got an notification message in the group.
|
|
325
|
+
* Also the @param {message.fromMe} is TRUE.
|
|
299
326
|
*
|
|
300
327
|
* 1.2 Otherwise:
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* Also
|
|
328
|
+
* @param {message.from} is a group chat id the event was emitted in.
|
|
329
|
+
* @param {message.to} is @type {undefined}.
|
|
330
|
+
* Also @param {message.fromMe} is FALSE.
|
|
304
331
|
*
|
|
305
332
|
* 2. If a notification was emitted due to a contact changing their phone number:
|
|
306
|
-
*
|
|
333
|
+
* @param {message.templateParams} is an array of two user's ids:
|
|
307
334
|
* the old (before the change) and a new one, stored in alphabetical order.
|
|
308
|
-
*
|
|
335
|
+
* @param {message.from} is a current user's id that has a chat with a user,
|
|
309
336
|
* whos phone number was changed.
|
|
310
|
-
*
|
|
337
|
+
* @param {message.to} is a user's id (after the change), the current user has a chat with.
|
|
311
338
|
*/
|
|
312
339
|
});
|
|
313
340
|
|
package/index.d.ts
CHANGED
|
@@ -60,6 +60,9 @@ declare namespace WAWebJS {
|
|
|
60
60
|
/** Get contact instance by ID */
|
|
61
61
|
getContactById(contactId: string): Promise<Contact>
|
|
62
62
|
|
|
63
|
+
/** Get message by ID */
|
|
64
|
+
getMessageById(messageId: string): Promise<Message>
|
|
65
|
+
|
|
63
66
|
/** Get all current contact instances */
|
|
64
67
|
getContacts(): Promise<Contact[]>
|
|
65
68
|
|
|
@@ -71,6 +74,9 @@ declare namespace WAWebJS {
|
|
|
71
74
|
|
|
72
75
|
/** Get all current Labels */
|
|
73
76
|
getLabels(): Promise<Label[]>
|
|
77
|
+
|
|
78
|
+
/** Change labels in chats */
|
|
79
|
+
addOrRemoveLabels(labelIds: Array<number|string>, chatIds: Array<string>): Promise<void>
|
|
74
80
|
|
|
75
81
|
/** Get Label instance by ID */
|
|
76
82
|
getLabelById(labelId: string): Promise<Label>
|
|
@@ -242,6 +248,16 @@ declare namespace WAWebJS {
|
|
|
242
248
|
ack: MessageAck
|
|
243
249
|
) => void): this
|
|
244
250
|
|
|
251
|
+
/** Emitted when an ack event occurrs on message type */
|
|
252
|
+
on(event: 'message_edit', listener: (
|
|
253
|
+
/** The message that was affected */
|
|
254
|
+
message: Message,
|
|
255
|
+
/** New text message */
|
|
256
|
+
newBody: String,
|
|
257
|
+
/** Prev text message */
|
|
258
|
+
prevBody: String
|
|
259
|
+
) => void): this
|
|
260
|
+
|
|
245
261
|
/** Emitted when a chat unread count changes */
|
|
246
262
|
on(event: 'unread_count', listener: (
|
|
247
263
|
/** The chat that was affected */
|
|
@@ -365,6 +381,10 @@ declare namespace WAWebJS {
|
|
|
365
381
|
puppeteer?: puppeteer.PuppeteerNodeLaunchOptions & puppeteer.ConnectOptions
|
|
366
382
|
/** Determines how to save and restore sessions. Will use LegacySessionAuth if options.session is set. Otherwise, NoAuth will be used. */
|
|
367
383
|
authStrategy?: AuthStrategy,
|
|
384
|
+
/** The version of WhatsApp Web to use. Use options.webVersionCache to configure how the version is retrieved. */
|
|
385
|
+
webVersion?: string,
|
|
386
|
+
/** Determines how to retrieve the WhatsApp Web version specified in options.webVersion. */
|
|
387
|
+
webVersionCache?: WebCacheOptions,
|
|
368
388
|
/** How many times should the qrcode be refreshed before giving up
|
|
369
389
|
* @default 0 (disabled) */
|
|
370
390
|
qrMaxRetries?: number,
|
|
@@ -392,6 +412,24 @@ declare namespace WAWebJS {
|
|
|
392
412
|
proxyAuthentication?: {username: string, password: string} | undefined
|
|
393
413
|
}
|
|
394
414
|
|
|
415
|
+
export interface LocalWebCacheOptions {
|
|
416
|
+
type: 'local',
|
|
417
|
+
path?: string,
|
|
418
|
+
strict?: boolean
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export interface RemoteWebCacheOptions {
|
|
422
|
+
type: 'remote',
|
|
423
|
+
remotePath: string,
|
|
424
|
+
strict?: boolean
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export interface NoWebCacheOptions {
|
|
428
|
+
type: 'none'
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export type WebCacheOptions = NoWebCacheOptions | LocalWebCacheOptions | RemoteWebCacheOptions;
|
|
432
|
+
|
|
395
433
|
/**
|
|
396
434
|
* Base class which all authentication strategies extend
|
|
397
435
|
*/
|
|
@@ -545,6 +583,7 @@ declare namespace WAWebJS {
|
|
|
545
583
|
MESSAGE_REVOKED_EVERYONE = 'message_revoke_everyone',
|
|
546
584
|
MESSAGE_REVOKED_ME = 'message_revoke_me',
|
|
547
585
|
MESSAGE_ACK = 'message_ack',
|
|
586
|
+
MESSAGE_EDIT = 'message_edit',
|
|
548
587
|
MEDIA_UPLOADED = 'media_uploaded',
|
|
549
588
|
CONTACT_CHANGED = 'contact_changed',
|
|
550
589
|
GROUP_JOIN = 'group_join',
|
|
@@ -767,6 +806,10 @@ declare namespace WAWebJS {
|
|
|
767
806
|
businessOwnerJid?: string,
|
|
768
807
|
/** Product JID */
|
|
769
808
|
productId?: string,
|
|
809
|
+
/** Last edit time */
|
|
810
|
+
latestEditSenderTimestampMs?: number,
|
|
811
|
+
/** Last edit message author */
|
|
812
|
+
latestEditMsgKey?: MessageId,
|
|
770
813
|
/** Message buttons */
|
|
771
814
|
dynamicReplyButtons?: object,
|
|
772
815
|
/** Selected button ID */
|
|
@@ -824,6 +867,8 @@ declare namespace WAWebJS {
|
|
|
824
867
|
* Gets the reactions associated with the given message
|
|
825
868
|
*/
|
|
826
869
|
getReactions: () => Promise<ReactionList[]>,
|
|
870
|
+
/** Edits the current message */
|
|
871
|
+
edit: (content: MessageContent, options?: MessageEditOptions) => Promise<Message | null>,
|
|
827
872
|
}
|
|
828
873
|
|
|
829
874
|
/** ID that represents a message */
|
|
@@ -867,6 +912,8 @@ declare namespace WAWebJS {
|
|
|
867
912
|
sendMediaAsSticker?: boolean
|
|
868
913
|
/** Send media as document */
|
|
869
914
|
sendMediaAsDocument?: boolean
|
|
915
|
+
/** Send photo/video as a view once message */
|
|
916
|
+
isViewOnce?: boolean
|
|
870
917
|
/** Automatically parse vCards and send them as contacts */
|
|
871
918
|
parseVCards?: boolean
|
|
872
919
|
/** Image or videos caption */
|
|
@@ -889,6 +936,16 @@ declare namespace WAWebJS {
|
|
|
889
936
|
stickerCategories?: string[]
|
|
890
937
|
}
|
|
891
938
|
|
|
939
|
+
/** Options for editing a message */
|
|
940
|
+
export interface MessageEditOptions {
|
|
941
|
+
/** Show links preview. Has no effect on multi-device accounts. */
|
|
942
|
+
linkPreview?: boolean
|
|
943
|
+
/** Contacts that are being mentioned in the message */
|
|
944
|
+
mentions?: Contact[]
|
|
945
|
+
/** Extra options */
|
|
946
|
+
extra?: any
|
|
947
|
+
}
|
|
948
|
+
|
|
892
949
|
export interface MediaFromURLOptions {
|
|
893
950
|
client?: Client
|
|
894
951
|
filename?: string
|
|
@@ -1113,6 +1170,8 @@ declare namespace WAWebJS {
|
|
|
1113
1170
|
markUnread: () => Promise<void>
|
|
1114
1171
|
/** Returns array of all Labels assigned to this Chat */
|
|
1115
1172
|
getLabels: () => Promise<Label[]>
|
|
1173
|
+
/** Add or remove labels to this Chat */
|
|
1174
|
+
changeLabels: (labelIds: Array<string | number>) => Promise<void>
|
|
1116
1175
|
}
|
|
1117
1176
|
|
|
1118
1177
|
export interface MessageSearchOptions {
|
package/package.json
CHANGED
package/src/Client.js
CHANGED
|
@@ -10,6 +10,7 @@ const { WhatsWebURL, DefaultOptions, Events, WAState } = require('./util/Constan
|
|
|
10
10
|
const { ExposeStore, LoadUtils } = require('./util/Injected');
|
|
11
11
|
const ChatFactory = require('./factories/ChatFactory');
|
|
12
12
|
const ContactFactory = require('./factories/ContactFactory');
|
|
13
|
+
const WebCacheFactory = require('./webCache/WebCacheFactory');
|
|
13
14
|
const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List, Reaction, Chat } = require('./structures');
|
|
14
15
|
const LegacySessionAuth = require('./authStrategies/LegacySessionAuth');
|
|
15
16
|
const NoAuth = require('./authStrategies/NoAuth');
|
|
@@ -19,6 +20,8 @@ const NoAuth = require('./authStrategies/NoAuth');
|
|
|
19
20
|
* @extends {EventEmitter}
|
|
20
21
|
* @param {object} options - Client options
|
|
21
22
|
* @param {AuthStrategy} options.authStrategy - Determines how to save and restore sessions. Will use LegacySessionAuth if options.session is set. Otherwise, NoAuth will be used.
|
|
23
|
+
* @param {string} options.webVersion - The version of WhatsApp Web to use. Use options.webVersionCache to configure how the version is retrieved.
|
|
24
|
+
* @param {object} options.webVersionCache - Determines how to retrieve the WhatsApp Web version. Defaults to a local cache (LocalWebCache) that falls back to latest if the requested version is not found.
|
|
22
25
|
* @param {number} options.authTimeoutMs - Timeout for authentication selector in puppeteer
|
|
23
26
|
* @param {object} options.puppeteer - Puppeteer launch options. View docs here: https://github.com/puppeteer/puppeteer/
|
|
24
27
|
* @param {number} options.qrMaxRetries - How many times should the qrcode be refreshed before giving up
|
|
@@ -115,6 +118,7 @@ class Client extends EventEmitter {
|
|
|
115
118
|
this.pupPage = page;
|
|
116
119
|
|
|
117
120
|
await this.authStrategy.afterBrowserInitialized();
|
|
121
|
+
await this.initWebVersionCache();
|
|
118
122
|
|
|
119
123
|
await page.goto(WhatsWebURL, {
|
|
120
124
|
waitUntil: 'load',
|
|
@@ -234,9 +238,9 @@ class Client extends EventEmitter {
|
|
|
234
238
|
// Listens to qr token change
|
|
235
239
|
if (mut.type === 'attributes' && mut.attributeName === 'data-ref') {
|
|
236
240
|
window.qrChanged(mut.target.dataset.ref);
|
|
237
|
-
}
|
|
241
|
+
}
|
|
238
242
|
// Listens to retry button, when found, click it
|
|
239
|
-
if (mut.type === 'childList') {
|
|
243
|
+
else if (mut.type === 'childList') {
|
|
240
244
|
const retry_button = document.querySelector(selectors.QR_RETRY_BUTTON);
|
|
241
245
|
if (retry_button) retry_button.click();
|
|
242
246
|
}
|
|
@@ -310,7 +314,7 @@ class Client extends EventEmitter {
|
|
|
310
314
|
await page.exposeFunction('onAddMessageEvent', msg => {
|
|
311
315
|
if (msg.type === 'gp2') {
|
|
312
316
|
const notification = new GroupNotification(this, msg);
|
|
313
|
-
if (
|
|
317
|
+
if (['add', 'invite', 'linked_group_join'].includes(msg.subtype)) {
|
|
314
318
|
/**
|
|
315
319
|
* Emitted when a user joins the chat via invite link or is added by an admin.
|
|
316
320
|
* @event Client#group_join
|
|
@@ -392,18 +396,18 @@ class Client extends EventEmitter {
|
|
|
392
396
|
|
|
393
397
|
/**
|
|
394
398
|
* The event notification that is received when one of
|
|
395
|
-
* the group participants changes
|
|
399
|
+
* the group participants changes their phone number.
|
|
396
400
|
*/
|
|
397
401
|
const isParticipant = msg.type === 'gp2' && msg.subtype === 'modify';
|
|
398
402
|
|
|
399
403
|
/**
|
|
400
404
|
* The event notification that is received when one of
|
|
401
|
-
* the contacts changes
|
|
405
|
+
* the contacts changes their phone number.
|
|
402
406
|
*/
|
|
403
407
|
const isContact = msg.type === 'notification_template' && msg.subtype === 'change_number';
|
|
404
408
|
|
|
405
409
|
if (isParticipant || isContact) {
|
|
406
|
-
/**
|
|
410
|
+
/** @type {GroupNotification} object does not provide enough information about this event, so a @type {Message} object is used. */
|
|
407
411
|
const message = new Message(this, msg);
|
|
408
412
|
|
|
409
413
|
const newId = isParticipant ? msg.recipients[0] : msg.to;
|
|
@@ -580,12 +584,28 @@ class Client extends EventEmitter {
|
|
|
580
584
|
this.emit(Events.CHAT_ARCHIVED, new Chat(this, chat), currState, prevState);
|
|
581
585
|
});
|
|
582
586
|
|
|
587
|
+
await page.exposeFunction('onEditMessageEvent', (msg, newBody, prevBody) => {
|
|
588
|
+
|
|
589
|
+
if(msg.type === 'revoked'){
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Emitted when messages are edited
|
|
594
|
+
* @event Client#message_edit
|
|
595
|
+
* @param {Message} message
|
|
596
|
+
* @param {string} newBody
|
|
597
|
+
* @param {string} prevBody
|
|
598
|
+
*/
|
|
599
|
+
this.emit(Events.MESSAGE_EDIT, new Message(this, msg), newBody, prevBody);
|
|
600
|
+
});
|
|
601
|
+
|
|
583
602
|
await page.evaluate(() => {
|
|
584
603
|
window.Store.Msg.on('change', (msg) => { window.onChangeMessageEvent(window.WWebJS.getMessageModel(msg)); });
|
|
585
604
|
window.Store.Msg.on('change:type', (msg) => { window.onChangeMessageTypeEvent(window.WWebJS.getMessageModel(msg)); });
|
|
586
605
|
window.Store.Msg.on('change:ack', (msg, ack) => { window.onMessageAckEvent(window.WWebJS.getMessageModel(msg), ack); });
|
|
587
606
|
window.Store.Msg.on('change:isUnsentMedia', (msg, unsent) => { if (msg.id.fromMe && !unsent) window.onMessageMediaUploadedEvent(window.WWebJS.getMessageModel(msg)); });
|
|
588
607
|
window.Store.Msg.on('remove', (msg) => { if (msg.isNewMsg) window.onRemoveMessageEvent(window.WWebJS.getMessageModel(msg)); });
|
|
608
|
+
window.Store.Msg.on('change:body', (msg, newBody, prevBody) => { window.onEditMessageEvent(window.WWebJS.getMessageModel(msg), newBody, prevBody); });
|
|
589
609
|
window.Store.AppState.on('change:state', (_AppState, state) => { window.onAppStateChangedEvent(state); });
|
|
590
610
|
window.Store.Conn.on('change:battery', (state) => { window.onBatteryStateChangedEvent(state); });
|
|
591
611
|
window.Store.Call.on('add', (call) => { window.onIncomingCall(call); });
|
|
@@ -638,6 +658,35 @@ class Client extends EventEmitter {
|
|
|
638
658
|
});
|
|
639
659
|
}
|
|
640
660
|
|
|
661
|
+
async initWebVersionCache() {
|
|
662
|
+
const { type: webCacheType, ...webCacheOptions } = this.options.webVersionCache;
|
|
663
|
+
const webCache = WebCacheFactory.createWebCache(webCacheType, webCacheOptions);
|
|
664
|
+
|
|
665
|
+
const requestedVersion = this.options.webVersion;
|
|
666
|
+
const versionContent = await webCache.resolve(requestedVersion);
|
|
667
|
+
|
|
668
|
+
if(versionContent) {
|
|
669
|
+
await this.pupPage.setRequestInterception(true);
|
|
670
|
+
this.pupPage.on('request', async (req) => {
|
|
671
|
+
if(req.url() === WhatsWebURL) {
|
|
672
|
+
req.respond({
|
|
673
|
+
status: 200,
|
|
674
|
+
contentType: 'text/html',
|
|
675
|
+
body: versionContent
|
|
676
|
+
});
|
|
677
|
+
} else {
|
|
678
|
+
req.continue();
|
|
679
|
+
}
|
|
680
|
+
});
|
|
681
|
+
} else {
|
|
682
|
+
this.pupPage.on('response', async (res) => {
|
|
683
|
+
if(res.ok() && res.url() === WhatsWebURL) {
|
|
684
|
+
await webCache.persist(await res.text());
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
641
690
|
/**
|
|
642
691
|
* Closes the client
|
|
643
692
|
*/
|
|
@@ -689,6 +738,7 @@ class Client extends EventEmitter {
|
|
|
689
738
|
* @property {boolean} [sendVideoAsGif=false] - Send video as gif
|
|
690
739
|
* @property {boolean} [sendMediaAsSticker=false] - Send media as a sticker
|
|
691
740
|
* @property {boolean} [sendMediaAsDocument=false] - Send media as a document
|
|
741
|
+
* @property {boolean} [isViewOnce=false] - Send photo/video as a view once message
|
|
692
742
|
* @property {boolean} [parseVCards=true] - Automatically parse vCards and send them as contacts
|
|
693
743
|
* @property {string} [caption] - Image or video caption
|
|
694
744
|
* @property {string} [quotedMessageId] - Id of the message that is being quoted (or replied to)
|
|
@@ -699,7 +749,7 @@ class Client extends EventEmitter {
|
|
|
699
749
|
* @property {string[]} [stickerCategories=undefined] - Sets the categories of the sticker, (if sendMediaAsSticker is true). Provide emoji char array, can be null.
|
|
700
750
|
* @property {MessageMedia} [media] - Media to be sent
|
|
701
751
|
*/
|
|
702
|
-
|
|
752
|
+
|
|
703
753
|
/**
|
|
704
754
|
* Send a message to a specific chatId
|
|
705
755
|
* @param {string} chatId
|
|
@@ -709,6 +759,10 @@ class Client extends EventEmitter {
|
|
|
709
759
|
* @returns {Promise<Message>} Message that was just sent
|
|
710
760
|
*/
|
|
711
761
|
async sendMessage(chatId, content, options = {}) {
|
|
762
|
+
if (options.mentions && options.mentions.some(possiblyContact => possiblyContact instanceof Contact)) {
|
|
763
|
+
console.warn('Mentions with an array of Contact are now deprecated. See more at https://github.com/pedroslopez/whatsapp-web.js/pull/2166.');
|
|
764
|
+
options.mentions = options.mentions.map(a => a.id._serialized);
|
|
765
|
+
}
|
|
712
766
|
let internalOptions = {
|
|
713
767
|
linkPreview: options.linkPreview === false ? undefined : true,
|
|
714
768
|
sendAudioAsVoice: options.sendAudioAsVoice,
|
|
@@ -718,7 +772,7 @@ class Client extends EventEmitter {
|
|
|
718
772
|
caption: options.caption,
|
|
719
773
|
quotedMessageId: options.quotedMessageId,
|
|
720
774
|
parseVCards: options.parseVCards === false ? false : true,
|
|
721
|
-
mentionedJidList: Array.isArray(options.mentions) ? options.mentions
|
|
775
|
+
mentionedJidList: Array.isArray(options.mentions) ? options.mentions : [],
|
|
722
776
|
extraOptions: options.extra
|
|
723
777
|
};
|
|
724
778
|
|
|
@@ -726,10 +780,12 @@ class Client extends EventEmitter {
|
|
|
726
780
|
|
|
727
781
|
if (content instanceof MessageMedia) {
|
|
728
782
|
internalOptions.attachment = content;
|
|
783
|
+
internalOptions.isViewOnce = options.isViewOnce,
|
|
729
784
|
content = '';
|
|
730
785
|
} else if (options.media instanceof MessageMedia) {
|
|
731
786
|
internalOptions.attachment = options.media;
|
|
732
787
|
internalOptions.caption = content;
|
|
788
|
+
internalOptions.isViewOnce = options.isViewOnce,
|
|
733
789
|
content = '';
|
|
734
790
|
} else if (content instanceof Location) {
|
|
735
791
|
internalOptions.location = content;
|
|
@@ -748,7 +804,7 @@ class Client extends EventEmitter {
|
|
|
748
804
|
internalOptions.list = content;
|
|
749
805
|
content = '';
|
|
750
806
|
}
|
|
751
|
-
|
|
807
|
+
|
|
752
808
|
if (internalOptions.sendMediaAsSticker && internalOptions.attachment) {
|
|
753
809
|
internalOptions.attachment = await Util.formatToWebpSticker(
|
|
754
810
|
internalOptions.attachment, {
|
|
@@ -774,7 +830,7 @@ class Client extends EventEmitter {
|
|
|
774
830
|
|
|
775
831
|
return new Message(this, newMessage);
|
|
776
832
|
}
|
|
777
|
-
|
|
833
|
+
|
|
778
834
|
/**
|
|
779
835
|
* Searches for messages
|
|
780
836
|
* @param {string} query
|
|
@@ -842,6 +898,24 @@ class Client extends EventEmitter {
|
|
|
842
898
|
|
|
843
899
|
return ContactFactory.create(this, contact);
|
|
844
900
|
}
|
|
901
|
+
|
|
902
|
+
async getMessageById(messageId) {
|
|
903
|
+
const msg = await this.pupPage.evaluate(async messageId => {
|
|
904
|
+
let msg = window.Store.Msg.get(messageId);
|
|
905
|
+
if(msg) return window.WWebJS.getMessageModel(msg);
|
|
906
|
+
|
|
907
|
+
const params = messageId.split('_');
|
|
908
|
+
if(params.length !== 3) throw new Error('Invalid serialized message id specified');
|
|
909
|
+
|
|
910
|
+
let messagesObject = await window.Store.Msg.getMessagesById([messageId]);
|
|
911
|
+
if (messagesObject && messagesObject.messages.length) msg = messagesObject.messages[0];
|
|
912
|
+
|
|
913
|
+
if(msg) return window.WWebJS.getMessageModel(msg);
|
|
914
|
+
}, messageId);
|
|
915
|
+
|
|
916
|
+
if(msg) return new Message(this, msg);
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
845
919
|
|
|
846
920
|
/**
|
|
847
921
|
* Returns an object with information about the invite code's group
|
|
@@ -877,7 +951,8 @@ class Client extends EventEmitter {
|
|
|
877
951
|
if (inviteInfo.inviteCodeExp == 0) throw 'Expired invite code';
|
|
878
952
|
return this.pupPage.evaluate(async inviteInfo => {
|
|
879
953
|
let { groupId, fromId, inviteCode, inviteCodeExp } = inviteInfo;
|
|
880
|
-
|
|
954
|
+
let userWid = window.Store.WidFactory.createWid(fromId);
|
|
955
|
+
return await window.Store.JoinInviteV4.joinGroupViaInviteV4(inviteCode, String(inviteCodeExp), groupId, userWid);
|
|
881
956
|
}, inviteInfo);
|
|
882
957
|
}
|
|
883
958
|
|
|
@@ -1279,6 +1354,35 @@ class Client extends EventEmitter {
|
|
|
1279
1354
|
|
|
1280
1355
|
return success;
|
|
1281
1356
|
}
|
|
1357
|
+
|
|
1358
|
+
/**
|
|
1359
|
+
* Change labels in chats
|
|
1360
|
+
* @param {Array<number|string>} labelIds
|
|
1361
|
+
* @param {Array<string>} chatIds
|
|
1362
|
+
* @returns {Promise<void>}
|
|
1363
|
+
*/
|
|
1364
|
+
async addOrRemoveLabels(labelIds, chatIds) {
|
|
1365
|
+
|
|
1366
|
+
return this.pupPage.evaluate(async (labelIds, chatIds) => {
|
|
1367
|
+
if (['smba', 'smbi'].indexOf(window.Store.Conn.platform) === -1) {
|
|
1368
|
+
throw '[LT01] Only Whatsapp business';
|
|
1369
|
+
}
|
|
1370
|
+
const labels = window.WWebJS.getLabels().filter(e => labelIds.find(l => l == e.id) !== undefined);
|
|
1371
|
+
const chats = window.Store.Chat.filter(e => chatIds.includes(e.id._serialized));
|
|
1372
|
+
|
|
1373
|
+
let actions = labels.map(label => ({id: label.id, type: 'add'}));
|
|
1374
|
+
|
|
1375
|
+
chats.forEach(chat => {
|
|
1376
|
+
(chat.labels || []).forEach(n => {
|
|
1377
|
+
if (!actions.find(e => e.id == n)) {
|
|
1378
|
+
actions.push({id: n, type: 'remove'});
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
return await window.Store.Label.addOrRemoveLabels(actions, chats);
|
|
1384
|
+
}, labelIds, chatIds);
|
|
1385
|
+
}
|
|
1282
1386
|
}
|
|
1283
1387
|
|
|
1284
1388
|
module.exports = Client;
|
package/src/structures/Chat.js
CHANGED
|
@@ -261,6 +261,15 @@ class Chat extends Base {
|
|
|
261
261
|
async getLabels() {
|
|
262
262
|
return this.client.getChatLabels(this.id._serialized);
|
|
263
263
|
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Add or remove labels to this Chat
|
|
267
|
+
* @param {Array<number|string>} labelIds
|
|
268
|
+
* @returns {Promise<void>}
|
|
269
|
+
*/
|
|
270
|
+
async changeLabels(labelIds) {
|
|
271
|
+
return this.client.addOrRemoveLabels(labelIds, [this.id._serialized]);
|
|
272
|
+
}
|
|
264
273
|
}
|
|
265
274
|
|
|
266
275
|
module.exports = Chat;
|
|
@@ -156,9 +156,10 @@ class Contact extends Base {
|
|
|
156
156
|
|
|
157
157
|
await this.client.pupPage.evaluate(async (contactId) => {
|
|
158
158
|
const contact = window.Store.Contact.get(contactId);
|
|
159
|
-
await window.Store.BlockContact.blockContact(contact);
|
|
159
|
+
await window.Store.BlockContact.blockContact({contact});
|
|
160
160
|
}, this.id._serialized);
|
|
161
161
|
|
|
162
|
+
this.isBlocked = true;
|
|
162
163
|
return true;
|
|
163
164
|
}
|
|
164
165
|
|
|
@@ -174,6 +175,7 @@ class Contact extends Base {
|
|
|
174
175
|
await window.Store.BlockContact.unblockContact(contact);
|
|
175
176
|
}, this.id._serialized);
|
|
176
177
|
|
|
178
|
+
this.isBlocked = false;
|
|
177
179
|
return true;
|
|
178
180
|
}
|
|
179
181
|
|
|
@@ -7,6 +7,7 @@ const Order = require('./Order');
|
|
|
7
7
|
const Payment = require('./Payment');
|
|
8
8
|
const Reaction = require('./Reaction');
|
|
9
9
|
const {MessageTypes} = require('../util/Constants');
|
|
10
|
+
const {Contact} = require('./Contact');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Represents a Message on WhatsApp
|
|
@@ -167,8 +168,8 @@ class Message extends Base {
|
|
|
167
168
|
inviteCodeExp: data.inviteCodeExp,
|
|
168
169
|
groupId: data.inviteGrp,
|
|
169
170
|
groupName: data.inviteGrpName,
|
|
170
|
-
fromId: data.from._serialized,
|
|
171
|
-
toId: data.to._serialized
|
|
171
|
+
fromId: data.from?._serialized ? data.from._serialized : data.from,
|
|
172
|
+
toId: data.to?._serialized ? data.to._serialized : data.to
|
|
172
173
|
} : undefined;
|
|
173
174
|
|
|
174
175
|
/**
|
|
@@ -224,6 +225,16 @@ class Message extends Base {
|
|
|
224
225
|
this.productId = data.productId;
|
|
225
226
|
}
|
|
226
227
|
|
|
228
|
+
/** Last edit time */
|
|
229
|
+
if (data.latestEditSenderTimestampMs) {
|
|
230
|
+
this.latestEditSenderTimestampMs = data.latestEditSenderTimestampMs;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Last edit message author */
|
|
234
|
+
if (data.latestEditMsgKey) {
|
|
235
|
+
this.latestEditMsgKey = data.latestEditMsgKey;
|
|
236
|
+
}
|
|
237
|
+
|
|
227
238
|
/**
|
|
228
239
|
* Links included in the message.
|
|
229
240
|
* @type {Array<{link: string, isSuspicious: boolean}>}
|
|
@@ -576,6 +587,42 @@ class Message extends Base {
|
|
|
576
587
|
return reaction;
|
|
577
588
|
});
|
|
578
589
|
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Edits the current message.
|
|
593
|
+
* @param {string} content
|
|
594
|
+
* @param {MessageEditOptions} [options] - Options used when editing the message
|
|
595
|
+
* @returns {Promise<?Message>}
|
|
596
|
+
*/
|
|
597
|
+
async edit(content, options = {}) {
|
|
598
|
+
if (options.mentions && options.mentions.some(possiblyContact => possiblyContact instanceof Contact)) {
|
|
599
|
+
options.mentions = options.mentions.map(a => a.id._serialized);
|
|
600
|
+
}
|
|
601
|
+
let internalOptions = {
|
|
602
|
+
linkPreview: options.linkPreview === false ? undefined : true,
|
|
603
|
+
mentionedJidList: Array.isArray(options.mentions) ? options.mentions : [],
|
|
604
|
+
extraOptions: options.extra
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
if (!this.fromMe) {
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
const messageEdit = await this.client.pupPage.evaluate(async (msgId, message, options) => {
|
|
611
|
+
let msg = window.Store.Msg.get(msgId);
|
|
612
|
+
if (!msg) return null;
|
|
613
|
+
|
|
614
|
+
let catEdit = (msg.type === 'chat' && window.Store.MsgActionChecks.canEditText(msg));
|
|
615
|
+
if (catEdit) {
|
|
616
|
+
const msgEdit = await window.WWebJS.editMessage(msg, message, options);
|
|
617
|
+
return msgEdit.serialize();
|
|
618
|
+
}
|
|
619
|
+
return null;
|
|
620
|
+
}, this.id._serialized, content, internalOptions);
|
|
621
|
+
if (messageEdit) {
|
|
622
|
+
return new Message(this.client, messageEdit);
|
|
623
|
+
}
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
579
626
|
}
|
|
580
627
|
|
|
581
628
|
module.exports = Message;
|
package/src/util/Constants.js
CHANGED
|
@@ -7,6 +7,10 @@ exports.DefaultOptions = {
|
|
|
7
7
|
headless: true,
|
|
8
8
|
defaultViewport: null
|
|
9
9
|
},
|
|
10
|
+
webVersion: '2.2322.15',
|
|
11
|
+
webVersionCache: {
|
|
12
|
+
type: 'local',
|
|
13
|
+
},
|
|
10
14
|
authTimeoutMs: 0,
|
|
11
15
|
qrMaxRetries: 0,
|
|
12
16
|
takeoverOnConflict: false,
|
|
@@ -44,6 +48,7 @@ exports.Events = {
|
|
|
44
48
|
MESSAGE_REVOKED_EVERYONE: 'message_revoke_everyone',
|
|
45
49
|
MESSAGE_REVOKED_ME: 'message_revoke_me',
|
|
46
50
|
MESSAGE_ACK: 'message_ack',
|
|
51
|
+
MESSAGE_EDIT: 'message_edit',
|
|
47
52
|
UNREAD_COUNT: 'unread_count',
|
|
48
53
|
MESSAGE_REACTION: 'message_reaction',
|
|
49
54
|
MEDIA_UPLOADED: 'media_uploaded',
|
package/src/util/Injected.js
CHANGED
|
@@ -31,8 +31,11 @@ exports.ExposeStore = (moduleRaidStr) => {
|
|
|
31
31
|
window.Store.SendClear = window.mR.findModule('sendClear')[0];
|
|
32
32
|
window.Store.SendDelete = window.mR.findModule('sendDelete')[0];
|
|
33
33
|
window.Store.SendMessage = window.mR.findModule('addAndSendMsgToChat')[0];
|
|
34
|
+
window.Store.EditMessage = window.mR.findModule('addAndSendMessageEdit')[0];
|
|
34
35
|
window.Store.SendSeen = window.mR.findModule('sendSeen')[0];
|
|
35
36
|
window.Store.User = window.mR.findModule('getMaybeMeUser')[0];
|
|
37
|
+
window.Store.ContactMethods = window.mR.findModule('getUserid')[0];
|
|
38
|
+
window.Store.BusinessProfileCollection = window.mR.findModule('BusinessProfileCollection')[0].BusinessProfileCollection;
|
|
36
39
|
window.Store.UploadUtils = window.mR.findModule((module) => (module.default && module.default.encryptAndUpload) ? module.default : null)[0].default;
|
|
37
40
|
window.Store.UserConstructor = window.mR.findModule((module) => (module.default && module.default.prototype && module.default.prototype.isServer && module.default.prototype.isUser) ? module.default : null)[0].default;
|
|
38
41
|
window.Store.Validators = window.mR.findModule('findLinks')[0];
|
|
@@ -42,7 +45,7 @@ exports.ExposeStore = (moduleRaidStr) => {
|
|
|
42
45
|
window.Store.PresenceUtils = window.mR.findModule('sendPresenceAvailable')[0];
|
|
43
46
|
window.Store.ChatState = window.mR.findModule('sendChatStateComposing')[0];
|
|
44
47
|
window.Store.GroupParticipants = window.mR.findModule('promoteParticipants')[0];
|
|
45
|
-
window.Store.JoinInviteV4 = window.mR.findModule('
|
|
48
|
+
window.Store.JoinInviteV4 = window.mR.findModule('queryGroupInviteV4')[0];
|
|
46
49
|
window.Store.findCommonGroups = window.mR.findModule('findCommonGroups')[0].findCommonGroups;
|
|
47
50
|
window.Store.StatusUtils = window.mR.findModule('setMyStatus')[0];
|
|
48
51
|
window.Store.ConversationMsgs = window.mR.findModule('loadEarlierMsgs')[0];
|
|
@@ -119,8 +122,12 @@ exports.LoadUtils = () => {
|
|
|
119
122
|
forceDocument: options.sendMediaAsDocument,
|
|
120
123
|
forceGif: options.sendVideoAsGif
|
|
121
124
|
});
|
|
122
|
-
|
|
125
|
+
|
|
126
|
+
if (options.caption){
|
|
127
|
+
attOptions.caption = options.caption;
|
|
128
|
+
}
|
|
123
129
|
content = options.sendMediaAsSticker ? undefined : attOptions.preview;
|
|
130
|
+
attOptions.isViewOnce = options.isViewOnce;
|
|
124
131
|
|
|
125
132
|
delete options.attachment;
|
|
126
133
|
delete options.sendMediaAsSticker;
|
|
@@ -226,8 +233,8 @@ exports.LoadUtils = () => {
|
|
|
226
233
|
}
|
|
227
234
|
|
|
228
235
|
let listOptions = {};
|
|
229
|
-
if(options.list){
|
|
230
|
-
if(window.Store.Conn.platform === 'smba' || window.Store.Conn.platform === 'smbi'){
|
|
236
|
+
if (options.list) {
|
|
237
|
+
if (window.Store.Conn.platform === 'smba' || window.Store.Conn.platform === 'smbi') {
|
|
231
238
|
throw '[LT01] Whatsapp business can\'t send this yet';
|
|
232
239
|
}
|
|
233
240
|
listOptions = {
|
|
@@ -286,6 +293,40 @@ exports.LoadUtils = () => {
|
|
|
286
293
|
await window.Store.SendMessage.addAndSendMsgToChat(chat, message);
|
|
287
294
|
return window.Store.Msg.get(newMsgId._serialized);
|
|
288
295
|
};
|
|
296
|
+
|
|
297
|
+
window.WWebJS.editMessage = async (msg, content, options = {}) => {
|
|
298
|
+
|
|
299
|
+
const extraOptions = options.extraOptions || {};
|
|
300
|
+
delete options.extraOptions;
|
|
301
|
+
|
|
302
|
+
if (options.mentionedJidList) {
|
|
303
|
+
options.mentionedJidList = options.mentionedJidList.map(cId => window.Store.Contact.get(cId).id);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (options.linkPreview) {
|
|
307
|
+
options.linkPreview = null;
|
|
308
|
+
|
|
309
|
+
// Not supported yet by WhatsApp Web on MD
|
|
310
|
+
if(!window.Store.MDBackend) {
|
|
311
|
+
const link = window.Store.Validators.findLink(content);
|
|
312
|
+
if (link) {
|
|
313
|
+
const preview = await window.Store.Wap.queryLinkPreview(link.url);
|
|
314
|
+
preview.preview = true;
|
|
315
|
+
preview.subtype = 'url';
|
|
316
|
+
options = { ...options, ...preview };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
const internalOptions = {
|
|
323
|
+
...options,
|
|
324
|
+
...extraOptions
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
await window.Store.EditMessage.sendMessageEdit(msg, content, internalOptions);
|
|
328
|
+
return window.Store.Msg.get(msg.id._serialized);
|
|
329
|
+
};
|
|
289
330
|
|
|
290
331
|
window.WWebJS.toStickerData = async (mediaInfo) => {
|
|
291
332
|
if (mediaInfo.mimetype == 'image/webp') return mediaInfo;
|
|
@@ -434,7 +475,7 @@ exports.LoadUtils = () => {
|
|
|
434
475
|
|
|
435
476
|
res.lastMessage = null;
|
|
436
477
|
if (res.msgs && res.msgs.length) {
|
|
437
|
-
const lastMessage = window.Store.Msg.get(chat.lastReceivedKey._serialized);
|
|
478
|
+
const lastMessage = chat.lastReceivedKey ? window.Store.Msg.get(chat.lastReceivedKey._serialized) : null;
|
|
438
479
|
if (lastMessage) {
|
|
439
480
|
res.lastMessage = window.WWebJS.getMessageModel(lastMessage);
|
|
440
481
|
}
|
|
@@ -462,19 +503,56 @@ exports.LoadUtils = () => {
|
|
|
462
503
|
|
|
463
504
|
window.WWebJS.getContactModel = contact => {
|
|
464
505
|
let res = contact.serialize();
|
|
465
|
-
res.isBusiness = contact.isBusiness;
|
|
506
|
+
res.isBusiness = contact.isBusiness === undefined ? false : contact.isBusiness;
|
|
466
507
|
|
|
467
508
|
if (contact.businessProfile) {
|
|
468
509
|
res.businessProfile = contact.businessProfile.serialize();
|
|
469
510
|
}
|
|
470
511
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
res.
|
|
512
|
+
// TODO: remove useOldImplementation and its checks once all clients are updated to >= v2.2327.4
|
|
513
|
+
const useOldImplementation
|
|
514
|
+
= window.WWebJS.compareWwebVersions(window.Debug.VERSION, '<', '2.2327.4');
|
|
515
|
+
|
|
516
|
+
res.isMe = useOldImplementation
|
|
517
|
+
? contact.isMe
|
|
518
|
+
: window.Store.ContactMethods.getIsMe(contact);
|
|
519
|
+
res.isUser = useOldImplementation
|
|
520
|
+
? contact.isUser
|
|
521
|
+
: window.Store.ContactMethods.getIsUser(contact);
|
|
522
|
+
res.isGroup = useOldImplementation
|
|
523
|
+
? contact.isGroup
|
|
524
|
+
: window.Store.ContactMethods.getIsGroup(contact);
|
|
525
|
+
res.isWAContact = useOldImplementation
|
|
526
|
+
? contact.isWAContact
|
|
527
|
+
: window.Store.ContactMethods.getIsWAContact(contact);
|
|
528
|
+
res.isMyContact = useOldImplementation
|
|
529
|
+
? contact.isMyContact
|
|
530
|
+
: window.Store.ContactMethods.getIsMyContact(contact);
|
|
476
531
|
res.isBlocked = contact.isContactBlocked;
|
|
477
|
-
res.userid =
|
|
532
|
+
res.userid = useOldImplementation
|
|
533
|
+
? contact.userid
|
|
534
|
+
: window.Store.ContactMethods.getUserid(contact);
|
|
535
|
+
res.isEnterprise = useOldImplementation
|
|
536
|
+
? contact.isEnterprise
|
|
537
|
+
: window.Store.ContactMethods.getIsEnterprise(contact);
|
|
538
|
+
res.verifiedName = useOldImplementation
|
|
539
|
+
? contact.verifiedName
|
|
540
|
+
: window.Store.ContactMethods.getVerifiedName(contact);
|
|
541
|
+
res.verifiedLevel = useOldImplementation
|
|
542
|
+
? contact.verifiedLevel
|
|
543
|
+
: window.Store.ContactMethods.getVerifiedLevel(contact);
|
|
544
|
+
res.statusMute = useOldImplementation
|
|
545
|
+
? contact.statusMute
|
|
546
|
+
: window.Store.ContactMethods.getStatusMute(contact);
|
|
547
|
+
res.name = useOldImplementation
|
|
548
|
+
? contact.name
|
|
549
|
+
: window.Store.ContactMethods.getName(contact);
|
|
550
|
+
res.shortName = useOldImplementation
|
|
551
|
+
? contact.shortName
|
|
552
|
+
: window.Store.ContactMethods.getShortName(contact);
|
|
553
|
+
res.pushname = useOldImplementation
|
|
554
|
+
? contact.pushname
|
|
555
|
+
: window.Store.ContactMethods.getPushname(contact);
|
|
478
556
|
|
|
479
557
|
return res;
|
|
480
558
|
};
|
|
@@ -482,6 +560,8 @@ exports.LoadUtils = () => {
|
|
|
482
560
|
window.WWebJS.getContact = async contactId => {
|
|
483
561
|
const wid = window.Store.WidFactory.createWid(contactId);
|
|
484
562
|
const contact = await window.Store.Contact.find(wid);
|
|
563
|
+
const bizProfile = await window.Store.BusinessProfileCollection.fetchBizProfile(wid);
|
|
564
|
+
bizProfile.profileOptions && (contact.businessProfile = bizProfile);
|
|
485
565
|
return window.WWebJS.getContactModel(contact);
|
|
486
566
|
};
|
|
487
567
|
|
|
@@ -707,4 +787,46 @@ exports.LoadUtils = () => {
|
|
|
707
787
|
throw err;
|
|
708
788
|
}
|
|
709
789
|
};
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Inner function that compares between two WWeb versions. Its purpose is to help the developer to choose the correct code implementation depending on the comparison value and the WWeb version.
|
|
793
|
+
* @param {string} lOperand The left operand for the WWeb version string to compare with
|
|
794
|
+
* @param {string} operator The comparison operator
|
|
795
|
+
* @param {string} rOperand The right operand for the WWeb version string to compare with
|
|
796
|
+
* @returns {boolean} Boolean value that indicates the result of the comparison
|
|
797
|
+
*/
|
|
798
|
+
window.WWebJS.compareWwebVersions = (lOperand, operator, rOperand) => {
|
|
799
|
+
if (!['>', '>=', '<', '<=', '='].includes(operator)) {
|
|
800
|
+
throw class _ extends Error {
|
|
801
|
+
constructor(m) { super(m); this.name = 'CompareWwebVersionsError'; }
|
|
802
|
+
}('Invalid comparison operator is provided');
|
|
803
|
+
|
|
804
|
+
}
|
|
805
|
+
if (typeof lOperand !== 'string' || typeof rOperand !== 'string') {
|
|
806
|
+
throw class _ extends Error {
|
|
807
|
+
constructor(m) { super(m); this.name = 'CompareWwebVersionsError'; }
|
|
808
|
+
}('A non-string WWeb version type is provided');
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
lOperand = lOperand.replace(/-beta$/, '');
|
|
812
|
+
rOperand = rOperand.replace(/-beta$/, '');
|
|
813
|
+
|
|
814
|
+
while (lOperand.length !== rOperand.length) {
|
|
815
|
+
lOperand.length > rOperand.length
|
|
816
|
+
? rOperand = rOperand.concat('0')
|
|
817
|
+
: lOperand = lOperand.concat('0');
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
lOperand = Number(lOperand.replace(/\./g, ''));
|
|
821
|
+
rOperand = Number(rOperand.replace(/\./g, ''));
|
|
822
|
+
|
|
823
|
+
return (
|
|
824
|
+
operator === '>' ? lOperand > rOperand :
|
|
825
|
+
operator === '>=' ? lOperand >= rOperand :
|
|
826
|
+
operator === '<' ? lOperand < rOperand :
|
|
827
|
+
operator === '<=' ? lOperand <= rOperand :
|
|
828
|
+
operator === '=' ? lOperand === rOperand :
|
|
829
|
+
false
|
|
830
|
+
);
|
|
831
|
+
};
|
|
710
832
|
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
const { WebCache, VersionResolveError } = require('./WebCache');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* LocalWebCache - Fetches a WhatsApp Web version from a local file store
|
|
8
|
+
* @param {object} options - options
|
|
9
|
+
* @param {string} options.path - Path to the directory where cached versions are saved, default is: "./.wwebjs_cache/"
|
|
10
|
+
* @param {boolean} options.strict - If true, will throw an error if the requested version can't be fetched. If false, will resolve to the latest version.
|
|
11
|
+
*/
|
|
12
|
+
class LocalWebCache extends WebCache {
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
super();
|
|
15
|
+
|
|
16
|
+
this.path = options.path || './.wwebjs_cache/';
|
|
17
|
+
this.strict = options.strict || false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async resolve(version) {
|
|
21
|
+
const filePath = path.join(this.path, `${version}.html`);
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
if (this.strict) throw new VersionResolveError(`Couldn't load version ${version} from the cache`);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async persist(indexHtml) {
|
|
33
|
+
// extract version from index (e.g. manifest-2.2206.9.json -> 2.2206.9)
|
|
34
|
+
const version = indexHtml.match(/manifest-([\d\\.]+)\.json/)[1];
|
|
35
|
+
if(!version) return;
|
|
36
|
+
|
|
37
|
+
const filePath = path.join(this.path, `${version}.html`);
|
|
38
|
+
fs.mkdirSync(this.path, { recursive: true });
|
|
39
|
+
fs.writeFileSync(filePath, indexHtml);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = LocalWebCache;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const fetch = require('node-fetch');
|
|
2
|
+
const { WebCache, VersionResolveError } = require('./WebCache');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* RemoteWebCache - Fetches a WhatsApp Web version index from a remote server
|
|
6
|
+
* @param {object} options - options
|
|
7
|
+
* @param {string} options.remotePath - Endpoint that should be used to fetch the version index. Use {version} as a placeholder for the version number.
|
|
8
|
+
* @param {boolean} options.strict - If true, will throw an error if the requested version can't be fetched. If false, will resolve to the latest version. Defaults to false.
|
|
9
|
+
*/
|
|
10
|
+
class RemoteWebCache extends WebCache {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
super();
|
|
13
|
+
|
|
14
|
+
if (!options.remotePath) throw new Error('webVersionCache.remotePath is required when using the remote cache');
|
|
15
|
+
this.remotePath = options.remotePath;
|
|
16
|
+
this.strict = options.strict || false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async resolve(version) {
|
|
20
|
+
const remotePath = this.remotePath.replace('{version}', version);
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const cachedRes = await fetch(remotePath);
|
|
24
|
+
if (cachedRes.ok) {
|
|
25
|
+
return cachedRes.text();
|
|
26
|
+
}
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.error(`Error fetching version ${version} from remote`, err);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (this.strict) throw new VersionResolveError(`Couldn't load version ${version} from the archive`);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async persist() {
|
|
36
|
+
// Nothing to do here
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = RemoteWebCache;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default implementation of a web version cache that does nothing.
|
|
3
|
+
*/
|
|
4
|
+
class WebCache {
|
|
5
|
+
async resolve() { return null; }
|
|
6
|
+
async persist() { }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class VersionResolveError extends Error { }
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
WebCache,
|
|
13
|
+
VersionResolveError
|
|
14
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const RemoteWebCache = require('./RemoteWebCache');
|
|
2
|
+
const LocalWebCache = require('./LocalWebCache');
|
|
3
|
+
const { WebCache } = require('./WebCache');
|
|
4
|
+
|
|
5
|
+
const createWebCache = (type, options) => {
|
|
6
|
+
switch (type) {
|
|
7
|
+
case 'remote':
|
|
8
|
+
return new RemoteWebCache(options);
|
|
9
|
+
case 'local':
|
|
10
|
+
return new LocalWebCache(options);
|
|
11
|
+
case 'none':
|
|
12
|
+
return new WebCache();
|
|
13
|
+
default:
|
|
14
|
+
throw new Error(`Invalid WebCache type ${type}`);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
createWebCache,
|
|
20
|
+
};
|