whatsapp-web.js 1.28.0 → 1.28.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/example.js CHANGED
@@ -470,6 +470,10 @@ client.on('message', async msg => {
470
470
  console.log(statuses);
471
471
  const chat = await statuses[0]?.getChat(); // Get user chat of a first status
472
472
  console.log(chat);
473
+ } else if (msg.body === '!changeSync') {
474
+ // NOTE: this action will take effect after you restart the client.
475
+ const backgroundSync = await client.setBackgroundSync(true);
476
+ console.log(backgroundSync);
473
477
  }
474
478
  });
475
479
 
package/index.d.ts CHANGED
@@ -116,7 +116,7 @@ declare namespace WAWebJS {
116
116
  * @param chatId ID of the chat that will be muted
117
117
  * @param unmuteDate Date when the chat will be unmuted, leave as is to mute forever
118
118
  */
119
- muteChat(chatId: string, unmuteDate?: Date): Promise<void>
119
+ muteChat(chatId: string, unmuteDate?: Date): Promise<{ isMuted: boolean, muteExpiration: number }>
120
120
 
121
121
  /**
122
122
  * Request authentication via pairing code instead of QR code
@@ -180,6 +180,13 @@ declare namespace WAWebJS {
180
180
  */
181
181
  setAutoDownloadVideos(flag: boolean): Promise<void>
182
182
 
183
+ /**
184
+ * Changing the background synchronization setting.
185
+ * NOTE: this action will take effect after you restart the client.
186
+ * @param flag true/false on or off
187
+ */
188
+ setBackgroundSync(flag: boolean): Promise<void>
189
+
183
190
  /**
184
191
  * Get user device count by ID
185
192
  * Each WaWeb Connection counts as one device, and the phone (if exists) counts as one
@@ -190,12 +197,18 @@ declare namespace WAWebJS {
190
197
 
191
198
  /** Sync history conversation of the Chat */
192
199
  syncHistory(chatId: string): Promise<boolean>
200
+
201
+ /** Save new contact to user's addressbook or edit the existing one */
202
+ saveOrEditAddressbookContact(phoneNumber: string, firstName: string, lastName: string, syncToAddressbook?: boolean): Promise<ChatId>
203
+
204
+ /** Deletes the contact from user's addressbook */
205
+ deleteAddressbookContact(honeNumber: string): Promise<void>
193
206
 
194
207
  /** Changes and returns the archive state of the Chat */
195
208
  unarchiveChat(chatId: string): Promise<boolean>
196
209
 
197
210
  /** Unmutes the Chat */
198
- unmuteChat(chatId: string): Promise<void>
211
+ unmuteChat(chatId: string): Promise<{ isMuted: boolean, muteExpiration: number }>
199
212
 
200
213
  /** Sets the current user's profile picture */
201
214
  setProfilePicture(media: MessageMedia): Promise<boolean>
@@ -953,7 +966,7 @@ declare namespace WAWebJS {
953
966
  /** Accept the Group V4 Invite in message */
954
967
  acceptGroupV4Invite: () => Promise<{status: number}>,
955
968
  /** Deletes the message from the chat */
956
- delete: (everyone?: boolean) => Promise<void>,
969
+ delete: (everyone?: boolean, clearMedia?: boolean) => Promise<void>,
957
970
  /** Downloads and returns the attached message media */
958
971
  downloadMedia: () => Promise<MessageMedia>,
959
972
  /** Returns the Chat this message was sent in */
@@ -1026,7 +1039,10 @@ declare namespace WAWebJS {
1026
1039
  export class Location {
1027
1040
  latitude: string;
1028
1041
  longitude: string;
1029
- options?: LocationSendOptions;
1042
+ name?: string;
1043
+ address?: string;
1044
+ url?: string;
1045
+ description?: string;
1030
1046
 
1031
1047
  constructor(latitude: number, longitude: number, options?: LocationSendOptions)
1032
1048
  }
@@ -1452,10 +1468,10 @@ declare namespace WAWebJS {
1452
1468
  /** Loads chat messages, sorted from earliest to latest. */
1453
1469
  fetchMessages: (searchOptions: MessageSearchOptions) => Promise<Message[]>,
1454
1470
  /** Mutes this chat forever, unless a date is specified */
1455
- mute: (unmuteDate?: Date) => Promise<void>,
1471
+ mute: (unmuteDate?: Date) => Promise<{ isMuted: boolean, muteExpiration: number }>,
1456
1472
  /** Send a message to this chat */
1457
1473
  sendMessage: (content: MessageContent, options?: MessageSendOptions) => Promise<Message>,
1458
- /** Set the message as seen */
1474
+ /** Set the chat as seen */
1459
1475
  sendSeen: () => Promise<void>,
1460
1476
  /** Simulate recording audio in chat. This will last for 25 seconds */
1461
1477
  sendStateRecording: () => Promise<void>,
@@ -1464,7 +1480,7 @@ declare namespace WAWebJS {
1464
1480
  /** un-archives this chat */
1465
1481
  unarchive: () => Promise<void>,
1466
1482
  /** Unmutes this chat */
1467
- unmute: () => Promise<void>,
1483
+ unmute: () => Promise<{ isMuted: boolean, muteExpiration: number }>,
1468
1484
  /** Returns the Contact that corresponds to this Chat. */
1469
1485
  getContact: () => Promise<Contact>,
1470
1486
  /** Marks this Chat as unread */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whatsapp-web.js",
3
- "version": "1.28.0",
3
+ "version": "1.28.1-alpha.0",
4
4
  "description": "Library for interacting with the WhatsApp Web API ",
5
5
  "main": "./index.js",
6
6
  "typings": "./index.d.ts",
package/src/Client.js CHANGED
@@ -278,7 +278,7 @@ class Client extends EventEmitter {
278
278
  await this.authStrategy.beforeBrowserInitialized();
279
279
 
280
280
  const puppeteerOpts = this.options.puppeteer;
281
- if (puppeteerOpts && puppeteerOpts.browserWSEndpoint) {
281
+ if (puppeteerOpts && (puppeteerOpts.browserWSEndpoint || puppeteerOpts.browserURL)) {
282
282
  browser = await puppeteer.connect(puppeteerOpts);
283
283
  page = await browser.newPage();
284
284
  } else {
@@ -1193,25 +1193,38 @@ class Client extends EventEmitter {
1193
1193
  /**
1194
1194
  * Mutes this chat forever, unless a date is specified
1195
1195
  * @param {string} chatId ID of the chat that will be muted
1196
- * @param {?Date} unmuteDate Date when the chat will be unmuted, leave as is to mute forever
1196
+ * @param {?Date} unmuteDate Date when the chat will be unmuted, don't provide a value to mute forever
1197
+ * @returns {Promise<{isMuted: boolean, muteExpiration: number}>}
1197
1198
  */
1198
1199
  async muteChat(chatId, unmuteDate) {
1199
- unmuteDate = unmuteDate ? unmuteDate.getTime() / 1000 : -1;
1200
- await this.pupPage.evaluate(async (chatId, timestamp) => {
1201
- let chat = await window.Store.Chat.get(chatId);
1202
- await chat.mute.mute({expiration: timestamp, sendDevice:!0});
1203
- }, chatId, unmuteDate || -1);
1200
+ unmuteDate = unmuteDate ? Math.floor(unmuteDate.getTime() / 1000) : -1;
1201
+ return this._muteUnmuteChat(chatId, 'MUTE', unmuteDate);
1204
1202
  }
1205
1203
 
1206
1204
  /**
1207
1205
  * Unmutes the Chat
1208
1206
  * @param {string} chatId ID of the chat that will be unmuted
1207
+ * @returns {Promise<{isMuted: boolean, muteExpiration: number}>}
1209
1208
  */
1210
1209
  async unmuteChat(chatId) {
1211
- await this.pupPage.evaluate(async chatId => {
1212
- let chat = await window.Store.Chat.get(chatId);
1213
- await window.Store.Cmd.muteChat(chat, false);
1214
- }, chatId);
1210
+ return this._muteUnmuteChat(chatId, 'UNMUTE');
1211
+ }
1212
+
1213
+ /**
1214
+ * Internal method to mute or unmute the chat
1215
+ * @param {string} chatId ID of the chat that will be muted/unmuted
1216
+ * @param {string} action The action: 'MUTE' or 'UNMUTE'
1217
+ * @param {number} unmuteDateTs Timestamp at which the chat will be unmuted
1218
+ * @returns {Promise<{isMuted: boolean, muteExpiration: number}>}
1219
+ */
1220
+ async _muteUnmuteChat (chatId, action, unmuteDateTs) {
1221
+ return this.pupPage.evaluate(async (chatId, action, unmuteDateTs) => {
1222
+ const chat = window.Store.Chat.get(chatId) ?? await window.Store.Chat.find(chatId);
1223
+ action === 'MUTE'
1224
+ ? await chat.mute.mute({ expiration: unmuteDateTs, sendDevice: true })
1225
+ : await chat.mute.unmute({ sendDevice: true });
1226
+ return { isMuted: chat.mute.expiration !== 0, muteExpiration: chat.mute.expiration };
1227
+ }, chatId, action, unmuteDateTs || -1);
1215
1228
  }
1216
1229
 
1217
1230
  /**
@@ -1281,7 +1294,7 @@ class Client extends EventEmitter {
1281
1294
  */
1282
1295
  async resetState() {
1283
1296
  await this.pupPage.evaluate(() => {
1284
- window.Store.AppState.phoneWatchdog.shiftTimer.forceRunNow();
1297
+ window.Store.AppState.reconnect();
1285
1298
  });
1286
1299
  }
1287
1300
 
@@ -1721,6 +1734,23 @@ class Client extends EventEmitter {
1721
1734
  }, flag);
1722
1735
  }
1723
1736
 
1737
+ /**
1738
+ * Setting background synchronization.
1739
+ * NOTE: this action will take effect after you restart the client.
1740
+ * @param {boolean} flag true/false
1741
+ * @returns {Promise<boolean>}
1742
+ */
1743
+ async setBackgroundSync(flag) {
1744
+ return await this.pupPage.evaluate(async flag => {
1745
+ const backSync = window.Store.Settings.getGlobalOfflineNotifications();
1746
+ if (backSync === flag) {
1747
+ return flag;
1748
+ }
1749
+ await window.Store.Settings.setGlobalOfflineNotifications(flag);
1750
+ return flag;
1751
+ }, flag);
1752
+ }
1753
+
1724
1754
  /**
1725
1755
  * Get user device count by ID
1726
1756
  * Each WaWeb Connection counts as one device, and the phone (if exists) counts as one
@@ -1745,8 +1775,9 @@ class Client extends EventEmitter {
1745
1775
  */
1746
1776
  async syncHistory(chatId) {
1747
1777
  return await this.pupPage.evaluate(async (chatId) => {
1748
- const chat = await window.WWebJS.getChat(chatId);
1749
- if (chat.endOfHistoryTransferType === 0) {
1778
+ const chatWid = window.Store.WidFactory.createWid(chatId);
1779
+ const chat = window.Store.Chat.get(chatWid) ?? (await window.Store.Chat.find(chatWid));
1780
+ if (chat?.endOfHistoryTransferType === 0) {
1750
1781
  await window.Store.HistorySync.sendPeerDataOperationRequest(3, {
1751
1782
  chatId: chat.id
1752
1783
  });
@@ -1755,6 +1786,39 @@ class Client extends EventEmitter {
1755
1786
  return false;
1756
1787
  }, chatId);
1757
1788
  }
1789
+
1790
+ /**
1791
+ * Save new contact to user's addressbook or edit the existing one
1792
+ * @param {string} phoneNumber The contact's phone number in a format "17182222222", where "1" is a country code
1793
+ * @param {string} firstName
1794
+ * @param {string} lastName
1795
+ * @param {boolean} [syncToAddressbook = false] If set to true, the contact will also be saved to the user's address book on their phone. False by default
1796
+ * @returns {Promise<import('..').ChatId>} Object in a wid format
1797
+ */
1798
+ async saveOrEditAddressbookContact(phoneNumber, firstName, lastName, syncToAddressbook = false)
1799
+ {
1800
+ return await this.pupPage.evaluate(async (phoneNumber, firstName, lastName, syncToAddressbook) => {
1801
+ return await window.Store.AddressbookContactUtils.saveContactAction(
1802
+ phoneNumber,
1803
+ null,
1804
+ firstName,
1805
+ lastName,
1806
+ syncToAddressbook
1807
+ );
1808
+ }, phoneNumber, firstName, lastName, syncToAddressbook);
1809
+ }
1810
+
1811
+ /**
1812
+ * Deletes the contact from user's addressbook
1813
+ * @param {string} phoneNumber The contact's phone number in a format "17182222222", where "1" is a country code
1814
+ * @returns {Promise<void>}
1815
+ */
1816
+ async deleteAddressbookContact(phoneNumber)
1817
+ {
1818
+ return await this.pupPage.evaluate(async (phoneNumber) => {
1819
+ return await window.Store.AddressbookContactUtils.deleteContactAction(phoneNumber);
1820
+ }, phoneNumber);
1821
+ }
1758
1822
  }
1759
1823
 
1760
1824
  module.exports = Client;
@@ -43,7 +43,7 @@ class Broadcast extends Base {
43
43
  * Messages statuses
44
44
  * @type {Message[]}
45
45
  */
46
- this.msgs = data.msgs.map(msg => new Message(this.client, msg));
46
+ this.msgs = data.msgs?.map(msg => new Message(this.client, msg));
47
47
 
48
48
  return super._patch(data);
49
49
  }
@@ -95,7 +95,7 @@ class Chat extends Base {
95
95
  }
96
96
 
97
97
  /**
98
- * Set the message as seen
98
+ * Set the chat as seen
99
99
  * @returns {Promise<Boolean>} result
100
100
  */
101
101
  async sendSeen() {
@@ -154,17 +154,25 @@ class Chat extends Base {
154
154
 
155
155
  /**
156
156
  * Mutes this chat forever, unless a date is specified
157
- * @param {?Date} unmuteDate Date at which the Chat will be unmuted, leave as is to mute forever
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}>}
158
159
  */
159
160
  async mute(unmuteDate) {
160
- return this.client.muteChat(this.id._serialized, 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;
161
165
  }
162
166
 
163
167
  /**
164
168
  * Unmutes this chat
169
+ * @returns {Promise<{isMuted: boolean, muteExpiration: number}>}
165
170
  */
166
171
  async unmute() {
167
- return this.client.unmuteChat(this.id._serialized);
172
+ const result = await this.client.unmuteChat(this.id._serialized);
173
+ this.isMuted = result.isMuted;
174
+ this.muteExpiration = result.muteExpiration;
175
+ return result;
168
176
  }
169
177
 
170
178
  /**
@@ -6,6 +6,7 @@
6
6
  * @property {string} [name] Location name
7
7
  * @property {string} [address] Location address
8
8
  * @property {string} [url] URL address to be shown within a location message
9
+ * @property {string} [description] Location full description
9
10
  */
10
11
 
11
12
  /**
@@ -498,23 +498,26 @@ class Message extends Base {
498
498
  /**
499
499
  * Deletes a message from the chat
500
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
+ * @param {?boolean} [clearMedia = true] If true, any associated media will also be deleted from a device.
501
502
  */
502
- async delete(everyone) {
503
- await this.client.pupPage.evaluate(async (msgId, everyone) => {
503
+ async delete(everyone, clearMedia = true) {
504
+ await this.client.pupPage.evaluate(async (msgId, everyone, clearMedia) => {
504
505
  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
+ const chat = window.Store.Chat.get(msg.id.remote) || (await window.Store.Chat.find(msg.id.remote));
506
507
 
507
- const canRevoke = window.Store.MsgActionChecks.canSenderRevokeMsg(msg) || window.Store.MsgActionChecks.canAdminRevokeMsg(msg);
508
+ const canRevoke =
509
+ window.Store.MsgActionChecks.canSenderRevokeMsg(msg) || window.Store.MsgActionChecks.canAdminRevokeMsg(msg);
510
+
508
511
  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
- }
512
+ return window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.0')
513
+ ? window.Store.Cmd.sendRevokeMsgs(chat, { list: [msg], type: 'message' }, { clearMedia: clearMedia })
514
+ : window.Store.Cmd.sendRevokeMsgs(chat, [msg], { clearMedia: true, type: msg.id.fromMe ? 'Sender' : 'Admin' });
514
515
  }
515
516
 
516
- return window.Store.Cmd.sendDeleteMsgs(chat, [msg], true);
517
- }, this.id._serialized, everyone);
517
+ return window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.0')
518
+ ? window.Store.Cmd.sendDeleteMsgs(chat, { list: [msg], type: 'message' }, clearMedia)
519
+ : window.Store.Cmd.sendDeleteMsgs(chat, [msg], clearMedia);
520
+ }, this.id._serialized, everyone, clearMedia);
518
521
  }
519
522
 
520
523
  /**
@@ -84,7 +84,7 @@ exports.ExposeStore = () => {
84
84
  window.Store.LinkPreview = window.require('WAWebLinkPreviewChatAction');
85
85
  window.Store.Socket = window.require('WADeprecatedSendIq');
86
86
  window.Store.SocketWap = window.require('WAWap');
87
- window.Store.SearchContext = window.require('WAWebChatMessageSearch').getSearchContext;
87
+ window.Store.SearchContext = window.require('WAWebChatMessageSearch');
88
88
  window.Store.DrawerManager = window.require('WAWebDrawerManager').DrawerManager;
89
89
  window.Store.LidUtils = window.require('WAWebApiContact');
90
90
  window.Store.WidToJid = window.require('WAWebWidToJid');
@@ -93,14 +93,19 @@ exports.ExposeStore = () => {
93
93
  window.Store.pinUnpinMsg = window.require('WAWebSendPinMessageAction').sendPinInChatMsg;
94
94
  window.Store.QueryExist = window.require('WAWebQueryExistsJob').queryWidExists;
95
95
  window.Store.ReplyUtils = window.require('WAWebMsgReply');
96
- window.Store.Settings = window.require('WAWebUserPrefsGeneral');
97
96
  window.Store.BotSecret = window.require('WAWebBotMessageSecret');
98
97
  window.Store.BotProfiles = window.require('WAWebBotProfileCollection');
99
98
  window.Store.DeviceList = window.require('WAWebApiDeviceList');
100
99
  window.Store.HistorySync = window.require('WAWebSendNonMessageDataRequest');
101
- if (window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.1014111620'))
100
+ if (window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.1014111620'))
102
101
  window.Store.AddonReactionTable = window.require('WAWebAddonReactionTableMode').reactionTableMode;
103
102
 
103
+ window.Store.Settings = {
104
+ ...window.require('WAWebUserPrefsGeneral'),
105
+ ...window.require('WAWebUserPrefsNotifications'),
106
+ setPushname: window.require('WAWebSetPushnameConnAction').setPushname
107
+ };
108
+
104
109
  window.Store.NumberInfo = {
105
110
  ...window.require('WAPhoneUtils'),
106
111
  ...window.require('WAPhoneFindCC')
@@ -109,7 +114,6 @@ exports.ExposeStore = () => {
109
114
  window.Store.ForwardUtils = {
110
115
  ...window.require('WAWebForwardMessagesToChat')
111
116
  };
112
-
113
117
  window.Store.StickerTools = {
114
118
  ...window.require('WAWebImageUtils'),
115
119
  ...window.require('WAWebAddWebpMetadata')
@@ -137,6 +141,10 @@ exports.ExposeStore = () => {
137
141
  ...window.require('WAWebApiMembershipApprovalRequestStore'),
138
142
  ...window.require('WASmaxGroupsMembershipRequestsActionRPC')
139
143
  };
144
+ window.Store.AddressbookContactUtils = {
145
+ ...window.require('WAWebSaveContactAction'),
146
+ ...window.require('WAWebDeleteContactAction')
147
+ };
140
148
 
141
149
  if (!window.Store.Chat._find || !window.Store.Chat.findImpl) {
142
150
  window.Store.Chat._find = e => {
@@ -8,7 +8,7 @@ exports.LoadUtils = () => {
8
8
  let chat = window.Store.Chat.get(chatId);
9
9
 
10
10
  if (window.compareWwebVersions(window.Debug.VERSION, '>', '2.3000.0')) {
11
- return window.Store.ForwardUtils.forwardMessagesToChats([msg], [chat], false);
11
+ return window.Store.ForwardUtils.forwardMessagesToChats([msg], [chat], true);
12
12
  } else {
13
13
  return chat.forwardMessages([msg]);
14
14
  }
@@ -214,8 +214,12 @@ exports.LoadUtils = () => {
214
214
  delete options.invokedBotWid;
215
215
  }
216
216
 
217
- const meUser = window.Store.User.getMaybeMeUser();
217
+ let meUser = window.Store.User.getMaybeMeUser();
218
218
  const newId = await window.Store.MsgKey.newId();
219
+
220
+ if (chat.id.isGroup() && chat.groupMetadata.isLidAddressingMode) {
221
+ meUser = window.Store.User.getMaybeMeLidUser();
222
+ }
219
223
 
220
224
  const newMsgId = new window.Store.MsgKey({
221
225
  from: meUser,
@@ -457,13 +461,14 @@ exports.LoadUtils = () => {
457
461
  let res = chat.serialize();
458
462
  res.isGroup = false;
459
463
  res.formattedTitle = chat.formattedTitle;
460
- res.isMuted = chat.muteExpiration == 0 ? false : true;
464
+ res.isMuted = chat.mute?.expiration !== 0;
461
465
 
462
466
  if (chat.groupMetadata) {
463
467
  res.isGroup = true;
464
468
  const chatWid = window.Store.WidFactory.createWid((chat.id._serialized));
465
469
  await window.Store.GroupMetadata.update(chatWid);
466
470
  res.groupMetadata = chat.groupMetadata.serialize();
471
+ res.isReadOnly = chat.groupMetadata.announce;
467
472
  }
468
473
 
469
474
  res.lastMessage = null;
@@ -1020,9 +1025,8 @@ exports.LoadUtils = () => {
1020
1025
  };
1021
1026
 
1022
1027
  window.WWebJS.getStatusModel = status => {
1023
- let res = status.serialize();
1028
+ const res = status.serialize();
1024
1029
  delete res._msgs;
1025
- res.msgs = status._msgs.map(msg => window.WWebJS.getMessageModel(msg));
1026
1030
  return res;
1027
1031
  };
1028
1032
 
@@ -48,11 +48,11 @@ class InterfaceController {
48
48
  * @param {string} msgId ID of the message that will be scrolled to
49
49
  */
50
50
  async openChatWindowAt(msgId) {
51
- await this.pupPage.evaluate(async msgId => {
51
+ await this.pupPage.evaluate(async (msgId) => {
52
52
  const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
53
- let chat = await window.Store.Chat.find(msg.id.remote);
54
- let searchContext = await window.Store.SearchContext(chat,msg);
55
- await window.Store.Cmd.openChatAt(chat, searchContext);
53
+ const chat = window.Store.Chat.get(msg.id.remote) ?? await window.Store.Chat.find(msg.id.remote);
54
+ const searchContext = await window.Store.SearchContext.getSearchContext(chat, msg.id);
55
+ await window.Store.Cmd.openChatAt({ chat: chat, msgContext: searchContext });
56
56
  }, msgId);
57
57
  }
58
58