whatsapp-web-jf.js 1.31.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 (55) 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 +680 -0
  6. package/index.d.ts +2069 -0
  7. package/index.js +34 -0
  8. package/package.json +55 -0
  9. package/shell.js +36 -0
  10. package/src/Client.js +2188 -0
  11. package/src/authStrategies/BaseAuthStrategy.js +27 -0
  12. package/src/authStrategies/LocalAuth.js +58 -0
  13. package/src/authStrategies/NoAuth.js +12 -0
  14. package/src/authStrategies/RemoteAuth.js +210 -0
  15. package/src/factories/ChatFactory.js +21 -0
  16. package/src/factories/ContactFactory.js +16 -0
  17. package/src/structures/Base.js +22 -0
  18. package/src/structures/Broadcast.js +69 -0
  19. package/src/structures/BusinessContact.js +21 -0
  20. package/src/structures/Buttons.js +82 -0
  21. package/src/structures/Call.js +76 -0
  22. package/src/structures/Channel.js +382 -0
  23. package/src/structures/Chat.js +291 -0
  24. package/src/structures/ClientInfo.js +71 -0
  25. package/src/structures/Contact.js +208 -0
  26. package/src/structures/GroupChat.js +473 -0
  27. package/src/structures/GroupNotification.js +104 -0
  28. package/src/structures/Label.js +50 -0
  29. package/src/structures/List.js +79 -0
  30. package/src/structures/Location.js +62 -0
  31. package/src/structures/Message.js +704 -0
  32. package/src/structures/MessageMedia.js +111 -0
  33. package/src/structures/Order.js +52 -0
  34. package/src/structures/Payment.js +79 -0
  35. package/src/structures/Poll.js +44 -0
  36. package/src/structures/PollVote.js +61 -0
  37. package/src/structures/PrivateChat.js +13 -0
  38. package/src/structures/PrivateContact.js +13 -0
  39. package/src/structures/Product.js +68 -0
  40. package/src/structures/ProductMetadata.js +25 -0
  41. package/src/structures/Reaction.js +69 -0
  42. package/src/structures/index.js +26 -0
  43. package/src/util/Constants.js +176 -0
  44. package/src/util/Injected/AuthStore/AuthStore.js +17 -0
  45. package/src/util/Injected/AuthStore/LegacyAuthStore.js +22 -0
  46. package/src/util/Injected/LegacyStore.js +146 -0
  47. package/src/util/Injected/Store.js +215 -0
  48. package/src/util/Injected/Utils.js +1139 -0
  49. package/src/util/InterfaceController.js +126 -0
  50. package/src/util/Puppeteer.js +23 -0
  51. package/src/util/Util.js +186 -0
  52. package/src/webCache/LocalWebCache.js +40 -0
  53. package/src/webCache/RemoteWebCache.js +40 -0
  54. package/src/webCache/WebCache.js +14 -0
  55. package/src/webCache/WebCacheFactory.js +20 -0
@@ -0,0 +1,382 @@
1
+ 'use strict';
2
+
3
+ const Base = require('./Base');
4
+ const Message = require('./Message');
5
+
6
+ /**
7
+ * Channel ID structure
8
+ * @typedef {Object} ChannelId
9
+ * @property {string} server
10
+ * @property {string} user
11
+ * @property {string} _serialized
12
+ */
13
+
14
+ /**
15
+ * Represents a Channel on WhatsApp
16
+ * @extends {Base}
17
+ */
18
+ class Channel extends Base {
19
+ constructor(client, data) {
20
+ super(client);
21
+
22
+ if (data) this._patch(data);
23
+ }
24
+
25
+ _patch(data) {
26
+ this.channelMetadata = data.channelMetadata;
27
+
28
+ /**
29
+ * ID that represents the channel
30
+ * @type {ChannelId}
31
+ */
32
+ this.id = data.id;
33
+
34
+ /**
35
+ * Title of the channel
36
+ * @type {string}
37
+ */
38
+ this.name = data.name;
39
+
40
+ /**
41
+ * The channel description
42
+ * @type {string}
43
+ */
44
+ this.description = data.channelMetadata.description;
45
+
46
+ /**
47
+ * Indicates if it is a Channel
48
+ * @type {boolean}
49
+ */
50
+ this.isChannel = data.isChannel;
51
+
52
+ /**
53
+ * Indicates if it is a Group
54
+ * @type {boolean}
55
+ */
56
+ this.isGroup = data.isGroup;
57
+
58
+ /**
59
+ * Indicates if the channel is readonly
60
+ * @type {boolean}
61
+ */
62
+ this.isReadOnly = data.isReadOnly;
63
+
64
+ /**
65
+ * Amount of messages unread
66
+ * @type {number}
67
+ */
68
+ this.unreadCount = data.unreadCount;
69
+
70
+ /**
71
+ * Unix timestamp for when the last activity occurred
72
+ * @type {number}
73
+ */
74
+ this.timestamp = data.t;
75
+
76
+ /**
77
+ * Indicates if the channel is muted or not
78
+ * @type {boolean}
79
+ */
80
+ this.isMuted = data.isMuted;
81
+
82
+ /**
83
+ * Unix timestamp for when the mute expires
84
+ * @type {number}
85
+ */
86
+ this.muteExpiration = data.muteExpiration;
87
+
88
+ /**
89
+ * Last message in the channel
90
+ * @type {Message}
91
+ */
92
+ this.lastMessage = data.lastMessage ? new Message(super.client, data.lastMessage) : undefined;
93
+
94
+ return super._patch(data);
95
+ }
96
+
97
+ /**
98
+ * Gets the subscribers of the channel (only those who are in your contact list)
99
+ * @param {?number} limit Optional parameter to specify the limit of subscribers to retrieve
100
+ * @returns {Promise<{contact: Contact, role: string}[]>} Returns an array of objects that handle the subscribed contacts and their roles in the channel
101
+ */
102
+ async getSubscribers(limit) {
103
+ return await this.client.pupPage.evaluate(async (channelId, limit) => {
104
+ const channel = await window.WWebJS.getChat(channelId, { getAsModel: false });
105
+ if (!channel) return [];
106
+ !limit && (limit = window.Store.ChannelUtils.getMaxSubscriberNumber());
107
+ const response = await window.Store.ChannelSubscribers.mexFetchNewsletterSubscribers(channelId, limit);
108
+ const contacts = window.Store.ChannelSubscribers.getSubscribersInContacts(response.subscribers);
109
+ return Promise.all(contacts.map((obj) => ({
110
+ ...obj,
111
+ contact: window.WWebJS.getContactModel(obj.contact)
112
+ })));
113
+ }, this.id._serialized, limit);
114
+ }
115
+
116
+ /**
117
+ * Updates the channel subject
118
+ * @param {string} newSubject
119
+ * @returns {Promise<boolean>} Returns true if the subject was properly updated. This can return false if the user does not have the necessary permissions.
120
+ */
121
+ async setSubject(newSubject) {
122
+ const success = await this._setChannelMetadata({ name: newSubject }, { editName: true });
123
+ success && (this.name = newSubject);
124
+ return success;
125
+ }
126
+
127
+ /**
128
+ * Updates the channel description
129
+ * @param {string} newDescription
130
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
131
+ */
132
+ async setDescription(newDescription) {
133
+ const success = await this._setChannelMetadata({ description: newDescription }, { editDescription: true });
134
+ success && (this.description = newDescription);
135
+ return success;
136
+ }
137
+
138
+ /**
139
+ * Updates the channel profile picture
140
+ * @param {MessageMedia} newProfilePicture
141
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
142
+ */
143
+ async setProfilePicture(newProfilePicture) {
144
+ return await this._setChannelMetadata({ picture: newProfilePicture }, { editPicture: true });
145
+ }
146
+
147
+ /**
148
+ * Updates available reactions to use in the channel
149
+ *
150
+ * Valid values for passing to the method are:
151
+ * 0 for NONE reactions to be avaliable
152
+ * 1 for BASIC reactions to be available: 👍, ❤️, 😂, 😮, 😢, 🙏
153
+ * 2 for ALL reactions to be available
154
+ * @param {number} reactionCode
155
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
156
+ */
157
+ async setReactionSetting(reactionCode) {
158
+ if (![0, 1, 2].includes(reactionCode)) return false;
159
+ const reactionMapper = {
160
+ 0: 3,
161
+ 1: 1,
162
+ 2: 0
163
+ };
164
+ const success = await this._setChannelMetadata(
165
+ { reactionCodesSetting: reactionMapper[reactionCode] },
166
+ { editReactionCodesSetting: true }
167
+ );
168
+ success && (this.channelMetadata.reactionCodesSetting = reactionCode);
169
+ return success;
170
+ }
171
+
172
+ /**
173
+ * Mutes the channel
174
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
175
+ */
176
+ async mute() {
177
+ const success = await this._muteUnmuteChannel('MUTE');
178
+ if (success) {
179
+ this.isMuted = true;
180
+ this.muteExpiration = -1;
181
+ }
182
+ return success;
183
+ }
184
+
185
+ /**
186
+ * Unmutes the channel
187
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
188
+ */
189
+ async unmute() {
190
+ const success = await this._muteUnmuteChannel('UNMUTE');
191
+ if (success) {
192
+ this.isMuted = false;
193
+ this.muteExpiration = 0;
194
+ }
195
+ return success;
196
+ }
197
+
198
+ /**
199
+ * Message options
200
+ * @typedef {Object} MessageSendOptions
201
+ * @property {?string} caption Image or video caption
202
+ * @property {?string[]} mentions User IDs of user that will be mentioned in the message
203
+ * @property {?MessageMedia} media Image or video to be sent
204
+ */
205
+
206
+ /**
207
+ * Sends a message to this channel
208
+ * @param {string|MessageMedia} content
209
+ * @param {?MessageSendOptions} options
210
+ * @returns {Promise<Message>} Message that was just sent
211
+ */
212
+ async sendMessage(content, options) {
213
+ return this.client.sendMessage(this.id._serialized, content, options);
214
+ }
215
+
216
+ /**
217
+ * Sets the channel as seen
218
+ * @returns {Promise<boolean>}
219
+ */
220
+ async sendSeen() {
221
+ return this.client.sendSeen(this.id._serialized);
222
+ }
223
+
224
+ /**
225
+ * @typedef {Object} SendChannelAdminInviteOptions
226
+ * @property {?string} comment The comment to be added to an invitation
227
+ */
228
+
229
+ /**
230
+ * Sends a channel admin invitation to a user, allowing them to become an admin of the channel
231
+ * @param {string} chatId The ID of a user to send the channel admin invitation to
232
+ * @param {SendChannelAdminInviteOptions} options
233
+ * @returns {Promise<boolean>} Returns true if an invitation was sent successfully, false otherwise
234
+ */
235
+ async sendChannelAdminInvite(chatId, options = {}) {
236
+ return this.client.sendChannelAdminInvite(chatId, this.id._serialized, options);
237
+ }
238
+
239
+ /**
240
+ * Accepts a channel admin invitation and promotes the current user to a channel admin
241
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
242
+ */
243
+ async acceptChannelAdminInvite() {
244
+ return this.client.acceptChannelAdminInvite(this.id._serialized);
245
+ }
246
+
247
+ /**
248
+ * Revokes a channel admin invitation sent to a user by a channel owner
249
+ * @param {string} userId The user ID the invitation was sent to
250
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
251
+ */
252
+ async revokeChannelAdminInvite(userId) {
253
+ return this.client.revokeChannelAdminInvite(this.id._serialized, userId);
254
+ }
255
+
256
+ /**
257
+ * Demotes a channel admin to a regular subscriber (can be used also for self-demotion)
258
+ * @param {string} userId The user ID to demote
259
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
260
+ */
261
+ async demoteChannelAdmin(userId) {
262
+ return this.client.demoteChannelAdmin(this.id._serialized, userId);
263
+ }
264
+
265
+ /**
266
+ * Options for transferring a channel ownership to another user
267
+ * @typedef {Object} TransferChannelOwnershipOptions
268
+ * @property {boolean} [shouldDismissSelfAsAdmin = false] If true, after the channel ownership is being transferred to another user, the current user will be dismissed as a channel admin and will become to a channel subscriber.
269
+ */
270
+
271
+ /**
272
+ * Transfers a channel ownership to another user.
273
+ * Note: the user you are transferring the channel ownership to must be a channel admin.
274
+ * @param {string} newOwnerId
275
+ * @param {TransferChannelOwnershipOptions} options
276
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
277
+ */
278
+ async transferChannelOwnership(newOwnerId, options = {}) {
279
+ return this.client.transferChannelOwnership(this.id._serialized, newOwnerId, options);
280
+ }
281
+
282
+ /**
283
+ * Loads channel messages, sorted from earliest to latest
284
+ * @param {Object} searchOptions Options for searching messages. Right now only limit and fromMe is supported
285
+ * @param {Number} [searchOptions.limit] The amount of messages to return. If no limit is specified, the available messages will be returned. Note that the actual number of returned messages may be smaller if there aren't enough messages in the conversation. Set this to Infinity to load all messages
286
+ * @param {Boolean} [searchOptions.fromMe] Return only messages from the bot number or vise versa. To get all messages, leave the option undefined
287
+ * @returns {Promise<Array<Message>>}
288
+ */
289
+ async fetchMessages(searchOptions) {
290
+ let messages = await this.client.pupPage.evaluate(async (channelId, searchOptions) => {
291
+ const msgFilter = (m) => {
292
+ if (m.isNotification || m.type === 'newsletter_notification') {
293
+ return false; // dont include notification messages
294
+ }
295
+ if (searchOptions && searchOptions.fromMe !== undefined && m.id.fromMe !== searchOptions.fromMe) {
296
+ return false;
297
+ }
298
+ return true;
299
+ };
300
+
301
+ const channel = await window.WWebJS.getChat(channelId, { getAsModel: false });
302
+ let msgs = channel.msgs.getModelsArray().filter(msgFilter);
303
+
304
+ if (searchOptions && searchOptions.limit > 0) {
305
+ while (msgs.length < searchOptions.limit) {
306
+ const loadedMessages = await window.Store.ConversationMsgs.loadEarlierMsgs(channel);
307
+ if (!loadedMessages || !loadedMessages.length) break;
308
+ msgs = [...loadedMessages.filter(msgFilter), ...msgs];
309
+ }
310
+
311
+ if (msgs.length > searchOptions.limit) {
312
+ msgs.sort((a, b) => (a.t > b.t) ? 1 : -1);
313
+ msgs = msgs.splice(msgs.length - searchOptions.limit);
314
+ }
315
+ }
316
+
317
+ return msgs.map(m => window.WWebJS.getMessageModel(m));
318
+
319
+ }, this.id._serialized, searchOptions);
320
+
321
+ return messages.map((msg) => new Message(this.client, msg));
322
+ }
323
+
324
+ /**
325
+ * Deletes the channel you created
326
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
327
+ */
328
+ async deleteChannel() {
329
+ return this.client.deleteChannel(this.id._serialized);
330
+ }
331
+
332
+ /**
333
+ * Internal method to change the channel metadata
334
+ * @param {string|number|MessageMedia} value The new value to set
335
+ * @param {string} property The property of a channel metadata to change
336
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
337
+ */
338
+ async _setChannelMetadata(value, property) {
339
+ return await this.client.pupPage.evaluate(async (channelId, value, property) => {
340
+ const channel = await window.WWebJS.getChat(channelId, { getAsModel: false });
341
+ if (!channel) return false;
342
+ if (property.editPicture) {
343
+ value.picture = value.picture
344
+ ? await window.WWebJS.cropAndResizeImage(value.picture, {
345
+ asDataUrl: true,
346
+ mimetype: 'image/jpeg',
347
+ size: 640,
348
+ quality: 1
349
+ })
350
+ : null;
351
+ }
352
+ try {
353
+ await window.Store.ChannelUtils.editNewsletterMetadataAction(channel, property, value);
354
+ return true;
355
+ } catch (err) {
356
+ if (err.name === 'ServerStatusCodeError') return false;
357
+ throw err;
358
+ }
359
+ }, this.id._serialized, value, property);
360
+ }
361
+
362
+ /**
363
+ * Internal method to mute or unmute the channel
364
+ * @param {string} action The action: 'MUTE' or 'UNMUTE'
365
+ * @returns {Promise<boolean>} Returns true if the operation completed successfully, false otherwise
366
+ */
367
+ async _muteUnmuteChannel(action) {
368
+ return await this.client.pupPage.evaluate(async (channelId, action) => {
369
+ try {
370
+ action === 'MUTE'
371
+ ? await window.Store.ChannelUtils.muteNewsletter([channelId])
372
+ : await window.Store.ChannelUtils.unmuteNewsletter([channelId]);
373
+ return true;
374
+ } catch (err) {
375
+ if (err.name === 'ServerStatusCodeError') return false;
376
+ throw err;
377
+ }
378
+ }, this.id._serialized, action);
379
+ }
380
+ }
381
+
382
+ module.exports = Channel;
@@ -0,0 +1,291 @@
1
+ 'use strict';
2
+
3
+ const Base = require('./Base');
4
+ const Message = require('./Message');
5
+
6
+ /**
7
+ * Represents a Chat on WhatsApp
8
+ * @extends {Base}
9
+ */
10
+ class Chat extends Base {
11
+ constructor(client, data) {
12
+ super(client);
13
+
14
+ if (data) this._patch(data);
15
+ }
16
+
17
+ _patch(data) {
18
+ /**
19
+ * ID that represents the chat
20
+ * @type {object}
21
+ */
22
+ this.id = data.id;
23
+
24
+ /**
25
+ * Title of the chat
26
+ * @type {string}
27
+ */
28
+ this.name = data.formattedTitle;
29
+
30
+ /**
31
+ * Indicates if the Chat is a Group Chat
32
+ * @type {boolean}
33
+ */
34
+ this.isGroup = data.isGroup;
35
+
36
+ /**
37
+ * Indicates if the Chat is readonly
38
+ * @type {boolean}
39
+ */
40
+ this.isReadOnly = data.isReadOnly;
41
+
42
+ /**
43
+ * Amount of messages unread
44
+ * @type {number}
45
+ */
46
+ this.unreadCount = data.unreadCount;
47
+
48
+ /**
49
+ * Unix timestamp for when the last activity occurred
50
+ * @type {number}
51
+ */
52
+ this.timestamp = data.t;
53
+
54
+ /**
55
+ * Indicates if the Chat is archived
56
+ * @type {boolean}
57
+ */
58
+ this.archived = data.archive;
59
+
60
+ /**
61
+ * Indicates if the Chat is pinned
62
+ * @type {boolean}
63
+ */
64
+ this.pinned = !!data.pin;
65
+
66
+ /**
67
+ * Indicates if the chat is muted or not
68
+ * @type {boolean}
69
+ */
70
+ this.isMuted = data.isMuted;
71
+
72
+ /**
73
+ * Unix timestamp for when the mute expires
74
+ * @type {number}
75
+ */
76
+ this.muteExpiration = data.muteExpiration;
77
+
78
+ /**
79
+ * Last message fo chat
80
+ * @type {Message}
81
+ */
82
+ this.lastMessage = data.lastMessage ? new Message(this.client, data.lastMessage) : undefined;
83
+
84
+ return super._patch(data);
85
+ }
86
+
87
+ /**
88
+ * Send a message to this chat
89
+ * @param {string|MessageMedia|Location} content
90
+ * @param {MessageSendOptions} [options]
91
+ * @returns {Promise<Message>} Message that was just sent
92
+ */
93
+ async sendMessage(content, options) {
94
+ return this.client.sendMessage(this.id._serialized, content, options);
95
+ }
96
+
97
+ /**
98
+ * Sets the chat as seen
99
+ * @returns {Promise<Boolean>} result
100
+ */
101
+ async sendSeen() {
102
+ return this.client.sendSeen(this.id._serialized);
103
+ }
104
+
105
+ /**
106
+ * Clears all messages from the chat
107
+ * @returns {Promise<boolean>} result
108
+ */
109
+ async clearMessages() {
110
+ return this.client.pupPage.evaluate(chatId => {
111
+ return window.WWebJS.sendClearChat(chatId);
112
+ }, this.id._serialized);
113
+ }
114
+
115
+ /**
116
+ * Deletes the chat
117
+ * @returns {Promise<Boolean>} result
118
+ */
119
+ async delete() {
120
+ return this.client.pupPage.evaluate(chatId => {
121
+ return window.WWebJS.sendDeleteChat(chatId);
122
+ }, this.id._serialized);
123
+ }
124
+
125
+ /**
126
+ * Archives this chat
127
+ */
128
+ async archive() {
129
+ return this.client.archiveChat(this.id._serialized);
130
+ }
131
+
132
+ /**
133
+ * un-archives this chat
134
+ */
135
+ async unarchive() {
136
+ return this.client.unarchiveChat(this.id._serialized);
137
+ }
138
+
139
+ /**
140
+ * Pins this chat
141
+ * @returns {Promise<boolean>} New pin state. Could be false if the max number of pinned chats was reached.
142
+ */
143
+ async pin() {
144
+ return this.client.pinChat(this.id._serialized);
145
+ }
146
+
147
+ /**
148
+ * Unpins this chat
149
+ * @returns {Promise<boolean>} New pin state
150
+ */
151
+ async unpin() {
152
+ return this.client.unpinChat(this.id._serialized);
153
+ }
154
+
155
+ /**
156
+ * Mutes this chat forever, unless a date is specified
157
+ * @param {?Date} unmuteDate Date when the chat will be unmuted, don't provide a value to mute forever
158
+ * @returns {Promise<{isMuted: boolean, muteExpiration: number}>}
159
+ */
160
+ async mute(unmuteDate) {
161
+ const result = await this.client.muteChat(this.id._serialized, unmuteDate);
162
+ this.isMuted = result.isMuted;
163
+ this.muteExpiration = result.muteExpiration;
164
+ return result;
165
+ }
166
+
167
+ /**
168
+ * Unmutes this chat
169
+ * @returns {Promise<{isMuted: boolean, muteExpiration: number}>}
170
+ */
171
+ async unmute() {
172
+ const result = await this.client.unmuteChat(this.id._serialized);
173
+ this.isMuted = result.isMuted;
174
+ this.muteExpiration = result.muteExpiration;
175
+ return result;
176
+ }
177
+
178
+ /**
179
+ * Mark this chat as unread
180
+ */
181
+ async markUnread(){
182
+ return this.client.markChatUnread(this.id._serialized);
183
+ }
184
+
185
+ /**
186
+ * Loads chat messages, sorted from earliest to latest.
187
+ * @param {Object} searchOptions Options for searching messages. Right now only limit and fromMe is supported.
188
+ * @param {Number} [searchOptions.limit] The amount of messages to return. If no limit is specified, the available messages will be returned. Note that the actual number of returned messages may be smaller if there aren't enough messages in the conversation. Set this to Infinity to load all messages.
189
+ * @param {Boolean} [searchOptions.fromMe] Return only messages from the bot number or vise versa. To get all messages, leave the option undefined.
190
+ * @returns {Promise<Array<Message>>}
191
+ */
192
+ async fetchMessages(searchOptions) {
193
+ let messages = await this.client.pupPage.evaluate(async (chatId, searchOptions) => {
194
+ const msgFilter = (m) => {
195
+ if (m.isNotification) {
196
+ return false; // dont include notification messages
197
+ }
198
+ if (searchOptions && searchOptions.fromMe !== undefined && m.id.fromMe !== searchOptions.fromMe) {
199
+ return false;
200
+ }
201
+ return true;
202
+ };
203
+
204
+ const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
205
+ let msgs = chat.msgs.getModelsArray().filter(msgFilter);
206
+
207
+ if (searchOptions && searchOptions.limit > 0) {
208
+ while (msgs.length < searchOptions.limit) {
209
+ const loadedMessages = await window.Store.ConversationMsgs.loadEarlierMsgs(chat);
210
+ if (!loadedMessages || !loadedMessages.length) break;
211
+ msgs = [...loadedMessages.filter(msgFilter), ...msgs];
212
+ }
213
+
214
+ if (msgs.length > searchOptions.limit) {
215
+ msgs.sort((a, b) => (a.t > b.t) ? 1 : -1);
216
+ msgs = msgs.splice(msgs.length - searchOptions.limit);
217
+ }
218
+ }
219
+
220
+ return msgs.map(m => window.WWebJS.getMessageModel(m));
221
+
222
+ }, this.id._serialized, searchOptions);
223
+
224
+ return messages.map(m => new Message(this.client, m));
225
+ }
226
+
227
+ /**
228
+ * Simulate typing in chat. This will last for 25 seconds.
229
+ */
230
+ async sendStateTyping() {
231
+ return this.client.pupPage.evaluate(chatId => {
232
+ window.WWebJS.sendChatstate('typing', chatId);
233
+ return true;
234
+ }, this.id._serialized);
235
+ }
236
+
237
+ /**
238
+ * Simulate recording audio in chat. This will last for 25 seconds.
239
+ */
240
+ async sendStateRecording() {
241
+ return this.client.pupPage.evaluate(chatId => {
242
+ window.WWebJS.sendChatstate('recording', chatId);
243
+ return true;
244
+ }, this.id._serialized);
245
+ }
246
+
247
+ /**
248
+ * Stops typing or recording in chat immediately.
249
+ */
250
+ async clearState() {
251
+ return this.client.pupPage.evaluate(chatId => {
252
+ window.WWebJS.sendChatstate('stop', chatId);
253
+ return true;
254
+ }, this.id._serialized);
255
+ }
256
+
257
+ /**
258
+ * Returns the Contact that corresponds to this Chat.
259
+ * @returns {Promise<Contact>}
260
+ */
261
+ async getContact() {
262
+ return await this.client.getContactById(this.id._serialized);
263
+ }
264
+
265
+ /**
266
+ * Returns array of all Labels assigned to this Chat
267
+ * @returns {Promise<Array<Label>>}
268
+ */
269
+ async getLabels() {
270
+ return this.client.getChatLabels(this.id._serialized);
271
+ }
272
+
273
+ /**
274
+ * Add or remove labels to this Chat
275
+ * @param {Array<number|string>} labelIds
276
+ * @returns {Promise<void>}
277
+ */
278
+ async changeLabels(labelIds) {
279
+ return this.client.addOrRemoveLabels(labelIds, [this.id._serialized]);
280
+ }
281
+
282
+ /**
283
+ * Sync chat history conversation
284
+ * @return {Promise<boolean>} True if operation completed successfully, false otherwise.
285
+ */
286
+ async syncHistory() {
287
+ return this.client.syncHistory(this.id._serialized);
288
+ }
289
+ }
290
+
291
+ module.exports = Chat;