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.
- package/.env.example +3 -0
- package/CODE_OF_CONDUCT.md +133 -0
- package/LICENSE +201 -0
- package/README.md +185 -0
- package/example.js +634 -0
- package/index.d.ts +1842 -0
- package/index.js +32 -0
- package/package.json +55 -0
- package/shell.js +36 -0
- package/src/Client.js +1747 -0
- package/src/authStrategies/BaseAuthStrategy.js +27 -0
- package/src/authStrategies/LocalAuth.js +56 -0
- package/src/authStrategies/NoAuth.js +12 -0
- package/src/authStrategies/RemoteAuth.js +204 -0
- package/src/factories/ChatFactory.js +16 -0
- package/src/factories/ContactFactory.js +16 -0
- package/src/structures/Base.js +22 -0
- package/src/structures/BusinessContact.js +21 -0
- package/src/structures/Buttons.js +82 -0
- package/src/structures/Call.js +76 -0
- package/src/structures/Chat.js +275 -0
- package/src/structures/ClientInfo.js +71 -0
- package/src/structures/Contact.js +208 -0
- package/src/structures/GroupChat.js +475 -0
- package/src/structures/GroupNotification.js +104 -0
- package/src/structures/Label.js +50 -0
- package/src/structures/List.js +79 -0
- package/src/structures/Location.js +61 -0
- package/src/structures/Message.js +711 -0
- package/src/structures/MessageMedia.js +111 -0
- package/src/structures/Order.js +52 -0
- package/src/structures/Payment.js +79 -0
- package/src/structures/Poll.js +44 -0
- package/src/structures/PollVote.js +61 -0
- package/src/structures/PrivateChat.js +13 -0
- package/src/structures/PrivateContact.js +13 -0
- package/src/structures/Product.js +68 -0
- package/src/structures/ProductMetadata.js +25 -0
- package/src/structures/Reaction.js +69 -0
- package/src/structures/index.js +24 -0
- package/src/util/Constants.js +176 -0
- package/src/util/Injected/AuthStore/AuthStore.js +17 -0
- package/src/util/Injected/AuthStore/LegacyAuthStore.js +22 -0
- package/src/util/Injected/LegacyStore.js +146 -0
- package/src/util/Injected/Store.js +167 -0
- package/src/util/Injected/Utils.js +1017 -0
- package/src/util/InterfaceController.js +127 -0
- package/src/util/Util.js +186 -0
- package/src/webCache/LocalWebCache.js +40 -0
- package/src/webCache/RemoteWebCache.js +40 -0
- package/src/webCache/WebCache.js +14 -0
- 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;
|