whatsapp-web-sj.js 1.26.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.
Files changed (52) hide show
  1. package/.env.example +3 -0
  2. package/CODE_OF_CONDUCT.md +133 -0
  3. package/LICENSE +201 -0
  4. package/README.md +185 -0
  5. package/example.js +634 -0
  6. package/index.d.ts +1842 -0
  7. package/index.js +32 -0
  8. package/package.json +55 -0
  9. package/shell.js +36 -0
  10. package/src/Client.js +1747 -0
  11. package/src/authStrategies/BaseAuthStrategy.js +27 -0
  12. package/src/authStrategies/LocalAuth.js +56 -0
  13. package/src/authStrategies/NoAuth.js +12 -0
  14. package/src/authStrategies/RemoteAuth.js +204 -0
  15. package/src/factories/ChatFactory.js +16 -0
  16. package/src/factories/ContactFactory.js +16 -0
  17. package/src/structures/Base.js +22 -0
  18. package/src/structures/BusinessContact.js +21 -0
  19. package/src/structures/Buttons.js +82 -0
  20. package/src/structures/Call.js +76 -0
  21. package/src/structures/Chat.js +275 -0
  22. package/src/structures/ClientInfo.js +71 -0
  23. package/src/structures/Contact.js +208 -0
  24. package/src/structures/GroupChat.js +475 -0
  25. package/src/structures/GroupNotification.js +104 -0
  26. package/src/structures/Label.js +50 -0
  27. package/src/structures/List.js +79 -0
  28. package/src/structures/Location.js +61 -0
  29. package/src/structures/Message.js +711 -0
  30. package/src/structures/MessageMedia.js +111 -0
  31. package/src/structures/Order.js +52 -0
  32. package/src/structures/Payment.js +79 -0
  33. package/src/structures/Poll.js +44 -0
  34. package/src/structures/PollVote.js +61 -0
  35. package/src/structures/PrivateChat.js +13 -0
  36. package/src/structures/PrivateContact.js +13 -0
  37. package/src/structures/Product.js +68 -0
  38. package/src/structures/ProductMetadata.js +25 -0
  39. package/src/structures/Reaction.js +69 -0
  40. package/src/structures/index.js +24 -0
  41. package/src/util/Constants.js +176 -0
  42. package/src/util/Injected/AuthStore/AuthStore.js +17 -0
  43. package/src/util/Injected/AuthStore/LegacyAuthStore.js +22 -0
  44. package/src/util/Injected/LegacyStore.js +146 -0
  45. package/src/util/Injected/Store.js +167 -0
  46. package/src/util/Injected/Utils.js +1017 -0
  47. package/src/util/InterfaceController.js +127 -0
  48. package/src/util/Util.js +186 -0
  49. package/src/webCache/LocalWebCache.js +40 -0
  50. package/src/webCache/RemoteWebCache.js +40 -0
  51. package/src/webCache/WebCache.js +14 -0
  52. package/src/webCache/WebCacheFactory.js +20 -0
@@ -0,0 +1,711 @@
1
+ 'use strict';
2
+
3
+ const Base = require('./Base');
4
+ const MessageMedia = require('./MessageMedia');
5
+ const Location = require('./Location');
6
+ const Order = require('./Order');
7
+ const Payment = require('./Payment');
8
+ const Reaction = require('./Reaction');
9
+ const Contact = require('./Contact');
10
+ const { MessageTypes } = require('../util/Constants');
11
+
12
+ /**
13
+ * Represents a Message on WhatsApp
14
+ * @extends {Base}
15
+ */
16
+ class Message extends Base {
17
+ constructor(client, data) {
18
+ super(client);
19
+
20
+ if (data) this._patch(data);
21
+ }
22
+
23
+ _patch(data) {
24
+ this._data = data;
25
+
26
+ /**
27
+ * MediaKey that represents the sticker 'ID'
28
+ * @type {string}
29
+ */
30
+ this.mediaKey = data.mediaKey;
31
+
32
+ /**
33
+ * ID that represents the message
34
+ * @type {object}
35
+ */
36
+ this.id = data.id;
37
+
38
+ /**
39
+ * ACK status for the message
40
+ * @type {MessageAck}
41
+ */
42
+ this.ack = data.ack;
43
+
44
+ /**
45
+ * Indicates if the message has media available for download
46
+ * @type {boolean}
47
+ */
48
+ this.hasMedia = Boolean(data.mediaKey && data.directPath);
49
+
50
+ /**
51
+ * Message content
52
+ * @type {string}
53
+ */
54
+ this.body = this.hasMedia ? data.caption || '' : data.body || data.pollName || '';
55
+
56
+ /**
57
+ * Message type
58
+ * @type {MessageTypes}
59
+ */
60
+ this.type = data.type;
61
+
62
+ /**
63
+ * Unix timestamp for when the message was created
64
+ * @type {number}
65
+ */
66
+ this.timestamp = data.t;
67
+
68
+ /**
69
+ * ID for the Chat that this message was sent to, except if the message was sent by the current user.
70
+ * @type {string}
71
+ */
72
+ this.from = (typeof (data.from) === 'object' && data.from !== null) ? data.from._serialized : data.from;
73
+
74
+ /**
75
+ * ID for who this message is for.
76
+ *
77
+ * If the message is sent by the current user, it will be the Chat to which the message is being sent.
78
+ * If the message is sent by another user, it will be the ID for the current user.
79
+ * @type {string}
80
+ */
81
+ this.to = (typeof (data.to) === 'object' && data.to !== null) ? data.to._serialized : data.to;
82
+
83
+ /**
84
+ * If the message was sent to a group, this field will contain the user that sent the message.
85
+ * @type {string}
86
+ */
87
+ this.author = (typeof (data.author) === 'object' && data.author !== null) ? data.author._serialized : data.author;
88
+
89
+ /**
90
+ * String that represents from which device type the message was sent
91
+ * @type {string}
92
+ */
93
+ this.deviceType = typeof data.id.id === 'string' && data.id.id.length > 21 ? 'android' : typeof data.id.id === 'string' && data.id.id.substring(0, 2) === '3A' ? 'ios' : 'web';
94
+ /**
95
+ * Indicates if the message was forwarded
96
+ * @type {boolean}
97
+ */
98
+ this.isForwarded = data.isForwarded;
99
+
100
+ /**
101
+ * Indicates how many times the message was forwarded.
102
+ *
103
+ * The maximum value is 127.
104
+ * @type {number}
105
+ */
106
+ this.forwardingScore = data.forwardingScore || 0;
107
+
108
+ /**
109
+ * Indicates if the message is a status update
110
+ * @type {boolean}
111
+ */
112
+ this.isStatus = data.isStatusV3 || data.id.remote === 'status@broadcast';
113
+
114
+ /**
115
+ * Indicates if the message was starred
116
+ * @type {boolean}
117
+ */
118
+ this.isStarred = data.star;
119
+
120
+ /**
121
+ * Indicates if the message was a broadcast
122
+ * @type {boolean}
123
+ */
124
+ this.broadcast = data.broadcast;
125
+
126
+ /**
127
+ * Indicates if the message was sent by the current user
128
+ * @type {boolean}
129
+ */
130
+ this.fromMe = data.id.fromMe;
131
+
132
+ /**
133
+ * Indicates if the message was sent as a reply to another message.
134
+ * @type {boolean}
135
+ */
136
+ this.hasQuotedMsg = data.quotedMsg ? true : false;
137
+
138
+ /**
139
+ * Indicates whether there are reactions to the message
140
+ * @type {boolean}
141
+ */
142
+ this.hasReaction = data.hasReaction ? true : false;
143
+
144
+ /**
145
+ * Indicates the duration of the message in seconds
146
+ * @type {string}
147
+ */
148
+ this.duration = data.duration ? data.duration : undefined;
149
+
150
+ /**
151
+ * Location information contained in the message, if the message is type "location"
152
+ * @type {Location}
153
+ */
154
+ this.location = (() => {
155
+ if (data.type !== MessageTypes.LOCATION) {
156
+ return undefined;
157
+ }
158
+ let description;
159
+ if (data.loc && typeof data.loc === 'string') {
160
+ let splitted = data.loc.split('\n');
161
+ description = {
162
+ name: splitted[0],
163
+ address: splitted[1],
164
+ url: data.clientUrl
165
+ };
166
+ }
167
+ return new Location(data.lat, data.lng, description);
168
+ })();
169
+
170
+ /**
171
+ * List of vCards contained in the message.
172
+ * @type {Array<string>}
173
+ */
174
+ this.vCards = data.type === MessageTypes.CONTACT_CARD_MULTI ? data.vcardList.map((c) => c.vcard) : data.type === MessageTypes.CONTACT_CARD ? [data.body] : [];
175
+
176
+ /**
177
+ * Group Invite Data
178
+ * @type {object}
179
+ */
180
+ this.inviteV4 = data.type === MessageTypes.GROUP_INVITE ? {
181
+ inviteCode: data.inviteCode,
182
+ inviteCodeExp: data.inviteCodeExp,
183
+ groupId: data.inviteGrp,
184
+ groupName: data.inviteGrpName,
185
+ fromId: typeof data.from === 'object' && '_serialized' in data.from ? data.from._serialized : data.from,
186
+ toId: typeof data.to === 'object' && '_serialized' in data.to ? data.to._serialized : data.to
187
+ } : undefined;
188
+
189
+ /**
190
+ * @typedef {Object} Mention
191
+ * @property {string} server
192
+ * @property {string} user
193
+ * @property {string} _serialized
194
+ */
195
+
196
+ /**
197
+ * Indicates the mentions in the message body.
198
+ * @type {Mention[]}
199
+ */
200
+ this.mentionedIds = data.mentionedJidList || [];
201
+
202
+ /**
203
+ * @typedef {Object} GroupMention
204
+ * @property {string} groupSubject The name of the group
205
+ * @property {Object} groupJid The group ID
206
+ * @property {string} groupJid.server
207
+ * @property {string} groupJid.user
208
+ * @property {string} groupJid._serialized
209
+ */
210
+
211
+ /**
212
+ * Indicates whether there are group mentions in the message body
213
+ * @type {GroupMention[]}
214
+ */
215
+ this.groupMentions = data.groupMentions || [];
216
+
217
+ /**
218
+ * Order ID for message type ORDER
219
+ * @type {string}
220
+ */
221
+ this.orderId = data.orderId ? data.orderId : undefined;
222
+ /**
223
+ * Order Token for message type ORDER
224
+ * @type {string}
225
+ */
226
+ this.token = data.token ? data.token : undefined;
227
+
228
+ /**
229
+ * Indicates whether the message is a Gif
230
+ * @type {boolean}
231
+ */
232
+ this.isGif = Boolean(data.isGif);
233
+
234
+ /**
235
+ * Indicates if the message will disappear after it expires
236
+ * @type {boolean}
237
+ */
238
+ this.isEphemeral = data.isEphemeral;
239
+
240
+ /** Title */
241
+ if (data.title) {
242
+ this.title = data.title;
243
+ }
244
+
245
+ /** Description */
246
+ if (data.description) {
247
+ this.description = data.description;
248
+ }
249
+
250
+ /** Business Owner JID */
251
+ if (data.businessOwnerJid) {
252
+ this.businessOwnerJid = data.businessOwnerJid;
253
+ }
254
+
255
+ /** Product ID */
256
+ if (data.productId) {
257
+ this.productId = data.productId;
258
+ }
259
+
260
+ /** Last edit time */
261
+ if (data.latestEditSenderTimestampMs) {
262
+ this.latestEditSenderTimestampMs = data.latestEditSenderTimestampMs;
263
+ }
264
+
265
+ /** Last edit message author */
266
+ if (data.latestEditMsgKey) {
267
+ this.latestEditMsgKey = data.latestEditMsgKey;
268
+ }
269
+
270
+ /**
271
+ * Links included in the message.
272
+ * @type {Array<{link: string, isSuspicious: boolean}>}
273
+ *
274
+ */
275
+ this.links = data.links;
276
+
277
+ /** Buttons */
278
+ if (data.dynamicReplyButtons) {
279
+ this.dynamicReplyButtons = data.dynamicReplyButtons;
280
+ }
281
+
282
+ /** Selected Button Id **/
283
+ if (data.selectedButtonId) {
284
+ this.selectedButtonId = data.selectedButtonId;
285
+ }
286
+
287
+ /** Selected List row Id **/
288
+ if (data.listResponse && data.listResponse.singleSelectReply.selectedRowId) {
289
+ this.selectedRowId = data.listResponse.singleSelectReply.selectedRowId;
290
+ }
291
+
292
+ if (this.type === MessageTypes.POLL_CREATION) {
293
+ this.pollName = data.pollName;
294
+ this.pollOptions = data.pollOptions;
295
+ this.allowMultipleAnswers = Boolean(!data.pollSelectableOptionsCount);
296
+ this.pollInvalidated = data.pollInvalidated;
297
+ this.isSentCagPollCreation = data.isSentCagPollCreation;
298
+ this.messageSecret = Object.keys(data.messageSecret).map((key) => data.messageSecret[key]);
299
+ }
300
+
301
+ return super._patch(data);
302
+ }
303
+
304
+ _getChatId() {
305
+ return this.fromMe ? this.to : this.from;
306
+ }
307
+
308
+ /**
309
+ * Reloads this Message object's data in-place with the latest values from WhatsApp Web.
310
+ * Note that the Message must still be in the web app cache for this to work, otherwise will return null.
311
+ * @returns {Promise<Message>}
312
+ */
313
+ async reload() {
314
+ const newData = await this.client.pupPage.evaluate(async (msgId) => {
315
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
316
+ if (!msg) return null;
317
+ return window.WWebJS.getMessageModel(msg);
318
+ }, this.id._serialized);
319
+
320
+ if(!newData) return null;
321
+
322
+ this._patch(newData);
323
+ return this;
324
+ }
325
+
326
+ /**
327
+ * Returns message in a raw format
328
+ * @type {Object}
329
+ */
330
+ get rawData() {
331
+ return this._data;
332
+ }
333
+
334
+ /**
335
+ * Returns the Chat this message was sent in
336
+ * @returns {Promise<Chat>}
337
+ */
338
+ getChat() {
339
+ return this.client.getChatById(this._getChatId());
340
+ }
341
+
342
+ /**
343
+ * Returns the Contact this message was sent from
344
+ * @returns {Promise<Contact>}
345
+ */
346
+ getContact() {
347
+ return this.client.getContactById(this.author || this.from);
348
+ }
349
+
350
+ /**
351
+ * Returns the Contacts mentioned in this message
352
+ * @returns {Promise<Array<Contact>>}
353
+ */
354
+ async getMentions() {
355
+ return await Promise.all(this.mentionedIds.map(async m => await this.client.getContactById(m)));
356
+ }
357
+
358
+ /**
359
+ * Returns groups mentioned in this message
360
+ * @returns {Promise<GroupChat[]|[]>}
361
+ */
362
+ async getGroupMentions() {
363
+ return await Promise.all(this.groupMentions.map(async (m) => await this.client.getChatById(m.groupJid._serialized)));
364
+ }
365
+
366
+ /**
367
+ * Returns the quoted message, if any
368
+ * @returns {Promise<Message>}
369
+ */
370
+ async getQuotedMessage() {
371
+ if (!this.hasQuotedMsg) return undefined;
372
+
373
+ const quotedMsg = await this.client.pupPage.evaluate(async (msgId) => {
374
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
375
+ const quotedMsg = window.Store.QuotedMsg.getQuotedMsgObj(msg);
376
+ return window.WWebJS.getMessageModel(quotedMsg);
377
+ }, this.id._serialized);
378
+
379
+ return new Message(this.client, quotedMsg);
380
+ }
381
+
382
+ /**
383
+ * Sends a message as a reply to this message. If chatId is specified, it will be sent
384
+ * through the specified Chat. If not, it will send the message
385
+ * in the same Chat as the original message was sent.
386
+ *
387
+ * @param {string|MessageMedia|Location} content
388
+ * @param {string} [chatId]
389
+ * @param {MessageSendOptions} [options]
390
+ * @returns {Promise<Message>}
391
+ */
392
+ async reply(content, chatId, options = {}) {
393
+ if (!chatId) {
394
+ chatId = this._getChatId();
395
+ }
396
+
397
+ options = {
398
+ ...options,
399
+ quotedMessageId: this.id._serialized
400
+ };
401
+
402
+ return this.client.sendMessage(chatId, content, options);
403
+ }
404
+
405
+ /**
406
+ * React to this message with an emoji
407
+ * @param {string} reaction - Emoji to react with. Send an empty string to remove the reaction.
408
+ * @return {Promise}
409
+ */
410
+ async react(reaction){
411
+ await this.client.pupPage.evaluate(async (messageId, reaction) => {
412
+ if (!messageId) return null;
413
+ const msg =
414
+ window.Store.Msg.get(messageId) || (await window.Store.Msg.getMessagesById([messageId]))?.messages?.[0];
415
+ if(!msg) return null;
416
+ await window.Store.sendReactionToMsg(msg, reaction);
417
+ }, this.id._serialized, reaction);
418
+ }
419
+
420
+ /**
421
+ * Accept Group V4 Invite
422
+ * @returns {Promise<Object>}
423
+ */
424
+ async acceptGroupV4Invite() {
425
+ return await this.client.acceptGroupV4Invite(this.inviteV4);
426
+ }
427
+
428
+ /**
429
+ * Forwards this message to another chat (that you chatted before, otherwise it will fail)
430
+ *
431
+ * @param {string|Chat} chat Chat model or chat ID to which the message will be forwarded
432
+ * @returns {Promise}
433
+ */
434
+ async forward(chat) {
435
+ const chatId = typeof chat === 'string' ? chat : chat.id._serialized;
436
+
437
+ await this.client.pupPage.evaluate(async (msgId, chatId) => {
438
+ return window.WWebJS.forwardMessage(chatId, msgId);
439
+ }, this.id._serialized, chatId);
440
+ }
441
+
442
+ /**
443
+ * Downloads and returns the attatched message media
444
+ * @returns {Promise<MessageMedia>}
445
+ */
446
+ async downloadMedia() {
447
+ if (!this.hasMedia) {
448
+ return undefined;
449
+ }
450
+
451
+ const result = await this.client.pupPage.evaluate(async (msgId) => {
452
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
453
+ if (!msg || !msg.mediaData) {
454
+ return null;
455
+ }
456
+ if (msg.mediaData.mediaStage != 'RESOLVED') {
457
+ // try to resolve media
458
+ await msg.downloadMedia({
459
+ downloadEvenIfExpensive: true,
460
+ rmrReason: 1
461
+ });
462
+ }
463
+
464
+ if (msg.mediaData.mediaStage.includes('ERROR') || msg.mediaData.mediaStage === 'FETCHING') {
465
+ // media could not be downloaded
466
+ return undefined;
467
+ }
468
+
469
+ try {
470
+ const decryptedMedia = await window.Store.DownloadManager.downloadAndMaybeDecrypt({
471
+ directPath: msg.directPath,
472
+ encFilehash: msg.encFilehash,
473
+ filehash: msg.filehash,
474
+ mediaKey: msg.mediaKey,
475
+ mediaKeyTimestamp: msg.mediaKeyTimestamp,
476
+ type: msg.type,
477
+ signal: (new AbortController).signal
478
+ });
479
+
480
+ const data = await window.WWebJS.arrayBufferToBase64Async(decryptedMedia);
481
+
482
+ return {
483
+ data,
484
+ mimetype: msg.mimetype,
485
+ filename: msg.filename,
486
+ filesize: msg.size
487
+ };
488
+ } catch (e) {
489
+ if(e.status && e.status === 404) return undefined;
490
+ throw e;
491
+ }
492
+ }, this.id._serialized);
493
+
494
+ if (!result) return undefined;
495
+ return new MessageMedia(result.mimetype, result.data, result.filename, result.filesize);
496
+ }
497
+
498
+ /**
499
+ * Deletes a message from the chat
500
+ * @param {?boolean} everyone If true and the message is sent by the current user or the user is an admin, will delete it for everyone in the chat.
501
+ */
502
+ async delete(everyone) {
503
+ await this.client.pupPage.evaluate(async (msgId, everyone) => {
504
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
505
+ let chat = await window.Store.Chat.find(msg.id.remote);
506
+
507
+ const canRevoke = window.Store.MsgActionChecks.canSenderRevokeMsg(msg) || window.Store.MsgActionChecks.canAdminRevokeMsg(msg);
508
+ if (everyone && canRevoke) {
509
+ if (window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.0')) {
510
+ return window.Store.Cmd.sendRevokeMsgs(chat, { list: [msg], type: 'message' }, { clearMedia: true });
511
+ } else {
512
+ return window.Store.Cmd.sendRevokeMsgs(chat, [msg], { clearMedia: true, type: msg.id.fromMe ? 'Sender' : 'Admin' });
513
+ }
514
+ }
515
+
516
+ return window.Store.Cmd.sendDeleteMsgs(chat, [msg], true);
517
+ }, this.id._serialized, everyone);
518
+ }
519
+
520
+ /**
521
+ * Stars this message
522
+ */
523
+ async star() {
524
+ await this.client.pupPage.evaluate(async (msgId) => {
525
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
526
+ if (window.Store.MsgActionChecks.canStarMsg(msg)) {
527
+ let chat = await window.Store.Chat.find(msg.id.remote);
528
+ return window.Store.Cmd.sendStarMsgs(chat, [msg], false);
529
+ }
530
+ }, this.id._serialized);
531
+ }
532
+
533
+ /**
534
+ * Unstars this message
535
+ */
536
+ async unstar() {
537
+ await this.client.pupPage.evaluate(async (msgId) => {
538
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
539
+ if (window.Store.MsgActionChecks.canStarMsg(msg)) {
540
+ let chat = await window.Store.Chat.find(msg.id.remote);
541
+ return window.Store.Cmd.sendUnstarMsgs(chat, [msg], false);
542
+ }
543
+ }, this.id._serialized);
544
+ }
545
+
546
+ /**
547
+ * Pins the message (group admins can pin messages of all group members)
548
+ * @param {number} duration The duration in seconds the message will be pinned in a chat
549
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
550
+ */
551
+ async pin(duration) {
552
+ return await this.client.pupPage.evaluate(async (msgId, duration) => {
553
+ return await window.WWebJS.pinUnpinMsgAction(msgId, 1, duration);
554
+ }, this.id._serialized, duration);
555
+ }
556
+
557
+ /**
558
+ * Unpins the message (group admins can unpin messages of all group members)
559
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
560
+ */
561
+ async unpin() {
562
+ return await this.client.pupPage.evaluate(async (msgId) => {
563
+ return await window.WWebJS.pinUnpinMsgAction(msgId, 2);
564
+ }, this.id._serialized);
565
+ }
566
+
567
+ /**
568
+ * Message Info
569
+ * @typedef {Object} MessageInfo
570
+ * @property {Array<{id: ContactId, t: number}>} delivery Contacts to which the message has been delivered to
571
+ * @property {number} deliveryRemaining Amount of people to whom the message has not been delivered to
572
+ * @property {Array<{id: ContactId, t: number}>} played Contacts who have listened to the voice message
573
+ * @property {number} playedRemaining Amount of people who have not listened to the message
574
+ * @property {Array<{id: ContactId, t: number}>} read Contacts who have read the message
575
+ * @property {number} readRemaining Amount of people who have not read the message
576
+ */
577
+
578
+ /**
579
+ * Get information about message delivery status.
580
+ * May return null if the message does not exist or is not sent by you.
581
+ * @returns {Promise<?MessageInfo>}
582
+ */
583
+ async getInfo() {
584
+ const info = await this.client.pupPage.evaluate(async (msgId) => {
585
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
586
+ if (!msg || !msg.id.fromMe) return null;
587
+
588
+ return new Promise((resolve) => {
589
+ setTimeout(async () => {
590
+ resolve(await window.Store.getMsgInfo(msg.id));
591
+ }, (Date.now() - msg.t * 1000 < 1250) && Math.floor(Math.random() * (1200 - 1100 + 1)) + 1100 || 0);
592
+ });
593
+ }, this.id._serialized);
594
+
595
+ return info;
596
+ }
597
+
598
+ /**
599
+ * Gets the order associated with a given message
600
+ * @return {Promise<Order>}
601
+ */
602
+ async getOrder() {
603
+ if (this.type === MessageTypes.ORDER) {
604
+ const result = await this.client.pupPage.evaluate((orderId, token, chatId) => {
605
+ return window.WWebJS.getOrderDetail(orderId, token, chatId);
606
+ }, this.orderId, this.token, this._getChatId());
607
+ if (!result) return undefined;
608
+ return new Order(this.client, result);
609
+ }
610
+ return undefined;
611
+ }
612
+ /**
613
+ * Gets the payment details associated with a given message
614
+ * @return {Promise<Payment>}
615
+ */
616
+ async getPayment() {
617
+ if (this.type === MessageTypes.PAYMENT) {
618
+ const msg = await this.client.pupPage.evaluate(async (msgId) => {
619
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
620
+ if(!msg) return null;
621
+ return msg.serialize();
622
+ }, this.id._serialized);
623
+ return new Payment(this.client, msg);
624
+ }
625
+ return undefined;
626
+ }
627
+
628
+
629
+ /**
630
+ * Reaction List
631
+ * @typedef {Object} ReactionList
632
+ * @property {string} id Original emoji
633
+ * @property {string} aggregateEmoji aggregate emoji
634
+ * @property {boolean} hasReactionByMe Flag who sent the reaction
635
+ * @property {Array<Reaction>} senders Reaction senders, to this message
636
+ */
637
+
638
+ /**
639
+ * Gets the reactions associated with the given message
640
+ * @return {Promise<ReactionList[]>}
641
+ */
642
+ async getReactions() {
643
+ if (!this.hasReaction) {
644
+ return undefined;
645
+ }
646
+
647
+ const reactions = await this.client.pupPage.evaluate(async (msgId) => {
648
+ const msgReactions = await window.Store.Reactions.find(msgId);
649
+ if (!msgReactions || !msgReactions.reactions.length) return null;
650
+ return msgReactions.reactions.serialize();
651
+ }, this.id._serialized);
652
+
653
+ if (!reactions) {
654
+ return undefined;
655
+ }
656
+
657
+ return reactions.map(reaction => {
658
+ reaction.senders = reaction.senders.map(sender => {
659
+ sender.timestamp = Math.round(sender.timestamp / 1000);
660
+ return new Reaction(this.client, sender);
661
+ });
662
+ return reaction;
663
+ });
664
+ }
665
+
666
+ /**
667
+ * Edits the current message.
668
+ * @param {string} content
669
+ * @param {MessageEditOptions} [options] - Options used when editing the message
670
+ * @returns {Promise<?Message>}
671
+ */
672
+ async edit(content, options = {}) {
673
+ if (options.mentions) {
674
+ !Array.isArray(options.mentions) && (options.mentions = [options.mentions]);
675
+ if (options.mentions.some((possiblyContact) => possiblyContact instanceof Contact)) {
676
+ console.warn('Mentions with an array of Contact are now deprecated. See more at https://github.com/pedroslopez/whatsapp-web.js/pull/2166.');
677
+ options.mentions = options.mentions.map((a) => a.id._serialized);
678
+ }
679
+ }
680
+
681
+ options.groupMentions && !Array.isArray(options.groupMentions) && (options.groupMentions = [options.groupMentions]);
682
+
683
+ let internalOptions = {
684
+ linkPreview: options.linkPreview === false ? undefined : true,
685
+ mentionedJidList: options.mentions || [],
686
+ groupMentions: options.groupMentions,
687
+ extraOptions: options.extra
688
+ };
689
+
690
+ if (!this.fromMe) {
691
+ return null;
692
+ }
693
+ const messageEdit = await this.client.pupPage.evaluate(async (msgId, message, options) => {
694
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
695
+ if (!msg) return null;
696
+
697
+ let canEdit = window.Store.MsgActionChecks.canEditText(msg) || window.Store.MsgActionChecks.canEditCaption(msg);
698
+ if (canEdit) {
699
+ const msgEdit = await window.WWebJS.editMessage(msg, message, options);
700
+ return msgEdit.serialize();
701
+ }
702
+ return null;
703
+ }, this.id._serialized, content, internalOptions);
704
+ if (messageEdit) {
705
+ return new Message(this.client, messageEdit);
706
+ }
707
+ return null;
708
+ }
709
+ }
710
+
711
+ module.exports = Message;