whatsapp-web.js 1.31.1-alpha.0 → 1.33.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.
@@ -7,6 +7,7 @@ const Order = require('./Order');
7
7
  const Payment = require('./Payment');
8
8
  const Reaction = require('./Reaction');
9
9
  const Contact = require('./Contact');
10
+ const ScheduledEvent = require('./ScheduledEvent'); // eslint-disable-line no-unused-vars
10
11
  const { MessageTypes } = require('../util/Constants');
11
12
 
12
13
  /**
@@ -51,7 +52,7 @@ class Message extends Base {
51
52
  * Message content
52
53
  * @type {string}
53
54
  */
54
- this.body = this.hasMedia ? data.caption || '' : data.body || data.pollName || '';
55
+ this.body = this.hasMedia ? data.caption || '' : data.body || data.pollName || data.eventName || '';
55
56
 
56
57
  /**
57
58
  * Message type
@@ -257,6 +258,14 @@ class Message extends Base {
257
258
  this.latestEditMsgKey = data.latestEditMsgKey;
258
259
  }
259
260
 
261
+ /**
262
+ * Protocol message key.
263
+ * Can be used to retrieve the ID of an original message that was revoked.
264
+ */
265
+ if (data.protocolMessageKey) {
266
+ this.protocolMessageKey = data.protocolMessageKey;
267
+ }
268
+
260
269
  /**
261
270
  * Links included in the message.
262
271
  * @type {Array<{link: string, isSuspicious: boolean}>}
@@ -699,6 +708,40 @@ class Message extends Base {
699
708
  }
700
709
  return null;
701
710
  }
711
+
712
+ /**
713
+ * Edits the current ScheduledEvent message.
714
+ * Once the scheduled event is canceled, it can not be edited.
715
+ * @param {ScheduledEvent} editedEventObject
716
+ * @returns {Promise<?Message>}
717
+ */
718
+ async editScheduledEvent(editedEventObject) {
719
+ if (!this.fromMe) {
720
+ return null;
721
+ }
722
+
723
+ const edittedEventMsg = await this.client.pupPage.evaluate(async (msgId, editedEventObject) => {
724
+ const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
725
+ if (!msg) return null;
726
+
727
+ const { name, startTimeTs, eventSendOptions } = editedEventObject;
728
+ const eventOptions = {
729
+ name: name,
730
+ description: eventSendOptions.description,
731
+ startTime: startTimeTs,
732
+ endTime: eventSendOptions.endTimeTs,
733
+ location: eventSendOptions.location,
734
+ callType: eventSendOptions.callType,
735
+ isEventCanceled: eventSendOptions.isEventCanceled,
736
+ };
737
+
738
+ await window.Store.ScheduledEventMsgUtils.sendEventEditMessage(eventOptions, msg);
739
+ const editedMsg = window.Store.Msg.get(msg.id._serialized);
740
+ return editedMsg?.serialize();
741
+ }, this.id._serialized, editedEventObject);
742
+
743
+ return edittedEventMsg && new Message(this.client, edittedEventMsg);
744
+ }
702
745
  }
703
746
 
704
747
  module.exports = Message;
@@ -34,14 +34,22 @@ class PollVote extends Base {
34
34
  * may occur when they deselected all poll options
35
35
  * @type {SelectedPollOption[]}
36
36
  */
37
- this.selectedOptions =
38
- data.selectedOptionLocalIds.length > 0
39
- ? data.selectedOptionLocalIds.map((e) => ({
37
+ if (data.selectedOptionLocalIds.length > 0) {
38
+ if(data.parentMessage) { // temporary failsafe
39
+ this.selectedOptions = data.selectedOptionLocalIds.map((e) => ({
40
40
  name: data.parentMessage.pollOptions.find((x) => x.localId === e).name,
41
41
  localId: e
42
- }))
43
- : [];
44
-
42
+ }));
43
+ } else {
44
+ this.selectedOptions = data.selectedOptionLocalIds.map((e) => ({
45
+ name: undefined,
46
+ localId: e
47
+ }));
48
+ }
49
+ } else {
50
+ this.selectedOptions = [];
51
+ }
52
+
45
53
  /**
46
54
  * Timestamp the option was selected or deselected at
47
55
  * @type {number}
@@ -54,6 +62,12 @@ class PollVote extends Base {
54
62
  */
55
63
  this.parentMessage = new Message(this.client, data.parentMessage);
56
64
 
65
+ /**
66
+ * The poll creation message id
67
+ * @type {Object}
68
+ */
69
+ this.parentMsgKey = data.parentMsgKey;
70
+
57
71
  return super._patch(data);
58
72
  }
59
73
  }
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ScheduledEvent send options
5
+ * @typedef {Object} ScheduledEventSendOptions
6
+ * @property {?string} description The scheduled event description
7
+ * @property {?Date} endTime The end time of the event
8
+ * @property {?string} location The location of the event
9
+ * @property {?string} callType The type of a WhatsApp call link to generate, valid values are: `video` | `voice`
10
+ * @property {boolean} [isEventCanceled = false] Indicates if a scheduled event should be sent as an already canceled
11
+ * @property {?Array<number>} messageSecret The custom message secret, can be used as an event ID. NOTE: it has to be a unique vector with a length of 32
12
+ */
13
+
14
+ /** Represents a ScheduledEvent on WhatsApp */
15
+ class ScheduledEvent {
16
+ /**
17
+ * @param {string} name
18
+ * @param {Date} startTime
19
+ * @param {ScheduledEventSendOptions} options
20
+ */
21
+ constructor(name, startTime, options = {}) {
22
+ /**
23
+ * The name of the event
24
+ * @type {string}
25
+ */
26
+ this.name = this._validateInputs('name', name).trim();
27
+
28
+ /**
29
+ * The start time of the event
30
+ * @type {number}
31
+ */
32
+ this.startTimeTs = Math.floor(startTime.getTime() / 1000);
33
+
34
+ /**
35
+ * The send options for the event
36
+ * @type {Object}
37
+ */
38
+ this.eventSendOptions = {
39
+ description: options.description?.trim(),
40
+ endTimeTs: options.endTime ? Math.floor(options.endTime.getTime() / 1000) : null,
41
+ location: options.location?.trim(),
42
+ callType: this._validateInputs('callType', options.callType),
43
+ isEventCanceled: options.isEventCanceled ?? false,
44
+ messageSecret: options.messageSecret
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Inner function to validate input values
50
+ * @param {string} propName The property name to validate the value of
51
+ * @param {string | number} propValue The property value to validate
52
+ * @returns {string | number} The property value if a validation succeeded
53
+ */
54
+ _validateInputs(propName, propValue) {
55
+ if (propName === 'name' && !propValue) {
56
+ throw new class CreateScheduledEventError extends Error {
57
+ constructor(m) { super(m); }
58
+ }(`Empty '${propName}' parameter value is provided.`);
59
+ }
60
+
61
+ if (propName === 'callType' && propValue && !['video', 'voice'].includes(propValue)) {
62
+ throw new class CreateScheduledEventError extends Error {
63
+ constructor(m) { super(m); }
64
+ }(`Invalid '${propName}' parameter value is provided. Valid values are: 'voice' | 'video'.`);
65
+ }
66
+
67
+ return propValue;
68
+ }
69
+ }
70
+
71
+ module.exports = ScheduledEvent;
@@ -22,5 +22,6 @@ module.exports = {
22
22
  Reaction: require('./Reaction'),
23
23
  Poll: require('./Poll'),
24
24
  PollVote: require('./PollVote'),
25
- Broadcast: require('./Broadcast')
25
+ Broadcast: require('./Broadcast'),
26
+ ScheduledEvent: require('./ScheduledEvent'),
26
27
  };
@@ -18,7 +18,12 @@ exports.DefaultOptions = {
18
18
  userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36',
19
19
  ffmpegPath: 'ffmpeg',
20
20
  bypassCSP: false,
21
- proxyAuthentication: undefined
21
+ proxyAuthentication: undefined,
22
+ pairWithPhoneNumber: {
23
+ phoneNumber: '',
24
+ showNotification: true,
25
+ intervalMs: 180000,
26
+ },
22
27
  };
23
28
 
24
29
  /**
@@ -60,6 +65,7 @@ exports.Events = {
60
65
  GROUP_MEMBERSHIP_REQUEST: 'group_membership_request',
61
66
  GROUP_UPDATE: 'group_update',
62
67
  QR_RECEIVED: 'qr',
68
+ CODE_RECEIVED: 'code',
63
69
  LOADING_SCREEN: 'loading_screen',
64
70
  DISCONNECTED: 'disconnected',
65
71
  STATE_CHANGED: 'change_state',
@@ -111,6 +117,7 @@ exports.MessageTypes = {
111
117
  REACTION: 'reaction',
112
118
  TEMPLATE_BUTTON_REPLY: 'template_button_reply',
113
119
  POLL_CREATION: 'poll_creation',
120
+ SCHEDULED_EVENT_CREATION: 'scheduled_event_creation',
114
121
  };
115
122
 
116
123
  /**
@@ -88,7 +88,6 @@ exports.ExposeStore = () => {
88
88
  window.Store.WidToJid = window.require('WAWebWidToJid');
89
89
  window.Store.JidToWid = window.require('WAWebJidToWid');
90
90
  window.Store.getMsgInfo = window.require('WAWebApiMessageInfoStore').queryMsgInfo;
91
- window.Store.pinUnpinMsg = window.require('WAWebSendPinMessageAction').sendPinInChatMsg;
92
91
  window.Store.QueryExist = window.require('WAWebQueryExistsJob').queryWidExists;
93
92
  window.Store.ReplyUtils = window.require('WAWebMsgReply');
94
93
  window.Store.BotSecret = window.require('WAWebBotMessageSecret');
@@ -97,8 +96,12 @@ exports.ExposeStore = () => {
97
96
  window.Store.DeviceList = window.require('WAWebApiDeviceList');
98
97
  window.Store.HistorySync = window.require('WAWebSendNonMessageDataRequest');
99
98
  window.Store.AddonReactionTable = window.require('WAWebAddonReactionTableMode').reactionTableMode;
99
+ window.Store.AddonPollVoteTable = window.require('WAWebAddonPollVoteTableMode').pollVoteTableMode;
100
+ window.Store.PinnedMsgUtils = window.require('WAWebPinInChatSchema');
100
101
  window.Store.ChatGetters = window.require('WAWebChatGetters');
102
+ window.Store.PinnedMsgUtils = window.require('WAWebSendPinMessageAction');
101
103
  window.Store.UploadUtils = window.require('WAWebUploadManager');
104
+ window.Store.WAWebStreamModel = window.require('WAWebStreamModel');
102
105
 
103
106
  window.Store.Settings = {
104
107
  ...window.require('WAWebUserPrefsGeneral'),
@@ -110,7 +113,12 @@ exports.ExposeStore = () => {
110
113
  ...window.require('WAPhoneFindCC')
111
114
  };
112
115
  window.Store.ForwardUtils = {
113
- ...window.require('WAWebForwardMessagesToChat')
116
+ ...window.require('WAWebChatForwardMessage')
117
+ };
118
+ window.Store.ScheduledEventMsgUtils = {
119
+ ...window.require('WAWebGenerateEventCallLink'),
120
+ ...window.require('WAWebSendEventEditMsgAction'),
121
+ ...window.require('WAWebSendEventResponseMsgAction')
114
122
  };
115
123
  window.Store.VCard = {
116
124
  ...window.require('WAWebFrontendVcardUtils'),
@@ -6,17 +6,13 @@ exports.LoadUtils = () => {
6
6
  window.WWebJS.forwardMessage = async (chatId, msgId) => {
7
7
  const msg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
8
8
  const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
9
-
10
- if (window.compareWwebVersions(window.Debug.VERSION, '>', '2.3000.0')) {
11
- return window.Store.ForwardUtils.forwardMessagesToChats([msg], [chat], true);
12
- } else {
13
- return chat.forwardMessages([msg]);
14
- }
9
+ return window.Store.ForwardUtils.forwardMessages(chat, [msg], true, true);
15
10
  };
16
11
 
17
12
  window.WWebJS.sendSeen = async (chatId) => {
18
13
  const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
19
14
  if (chat) {
15
+ window.Store.WAWebStreamModel.Stream.markAvailable();
20
16
  await window.Store.SendSeen.sendSeen(chat);
21
17
  return true;
22
18
  }
@@ -120,6 +116,34 @@ exports.LoadUtils = () => {
120
116
  delete options.poll;
121
117
  }
122
118
 
119
+ let eventOptions = {};
120
+ if (options.event) {
121
+ const { name, startTimeTs, eventSendOptions } = options.event;
122
+ const { messageSecret } = eventSendOptions;
123
+ eventOptions = {
124
+ type: 'event_creation',
125
+ eventName: name,
126
+ eventDescription: eventSendOptions.description,
127
+ eventStartTime: startTimeTs,
128
+ eventEndTime: eventSendOptions.endTimeTs,
129
+ eventLocation: eventSendOptions.location && {
130
+ degreesLatitude: 0,
131
+ degreesLongitude: 0,
132
+ name: eventSendOptions.location
133
+ },
134
+ eventJoinLink: await window.Store.ScheduledEventMsgUtils.createEventCallLink(
135
+ startTimeTs,
136
+ eventSendOptions.callType
137
+ ),
138
+ isEventCanceled: eventSendOptions.isEventCanceled,
139
+ messageSecret:
140
+ Array.isArray(messageSecret) && messageSecret.length === 32
141
+ ? new Uint8Array(messageSecret)
142
+ : window.crypto.getRandomValues(new Uint8Array(32)),
143
+ };
144
+ delete options.event;
145
+ }
146
+
123
147
  let vcardOptions = {};
124
148
  if (options.contactCard) {
125
149
  let contact = window.Store.Contact.get(options.contactCard);
@@ -223,9 +247,9 @@ exports.LoadUtils = () => {
223
247
  let from = chat.id.isLid() ? lidUser : meUser;
224
248
  let participant;
225
249
 
226
- if (chat.isGroup) {
250
+ if (typeof chat.id?.isGroup === 'function' && chat.id.isGroup()) {
227
251
  from = chat.groupMetadata && chat.groupMetadata.isLidAddressingMode ? lidUser : meUser;
228
- participant = window.Store.WidFactory.toUserWid(from);
252
+ participant = window.Store.WidFactory.toUserWidOrThrow(from);
229
253
  }
230
254
 
231
255
  const newMsgKey = new window.Store.MsgKey({
@@ -259,6 +283,7 @@ exports.LoadUtils = () => {
259
283
  ...quotedMsgOptions,
260
284
  ...locationOptions,
261
285
  ..._pollOptions,
286
+ ...eventOptions,
262
287
  ...vcardOptions,
263
288
  ...buttonOptions,
264
289
  ...listOptions,
@@ -301,10 +326,11 @@ exports.LoadUtils = () => {
301
326
  return msg;
302
327
  }
303
328
 
304
- await window.Store.SendMessage.addAndSendMsgToChat(chat, message);
305
- await window.Store.HistorySync.sendPeerDataOperationRequest(3, {
306
- chatId: chat.id
307
- });
329
+ const [msgPromise, sendMsgResultPromise] = window.Store.SendMessage.addAndSendMsgToChat(chat, message);
330
+ await msgPromise;
331
+
332
+ if (options.waitUntilMsgSent) await sendMsgResultPromise;
333
+
308
334
  return window.Store.Msg.get(newMsgKey._serialized);
309
335
  };
310
336
 
@@ -497,15 +523,6 @@ exports.LoadUtils = () => {
497
523
  return msg;
498
524
  };
499
525
 
500
- window.WWebJS.getPollVoteModel = async (vote) => {
501
- const _vote = vote.serialize();
502
- if (!vote.parentMsgKey) return null;
503
- const msg =
504
- window.Store.Msg.get(vote.parentMsgKey) || (await window.Store.Msg.getMessagesById([vote.parentMsgKey]))?.messages?.[0];
505
- msg && (_vote.parentMessage = window.WWebJS.getMessageModel(msg));
506
- return _vote;
507
- };
508
-
509
526
  window.WWebJS.getChat = async (chatId, { getAsModel = true } = {}) => {
510
527
  const isChannel = /@\w*newsletter\b/.test(chatId);
511
528
  const chatWid = window.Store.WidFactory.createWid(chatId);
@@ -870,8 +887,8 @@ exports.LoadUtils = () => {
870
887
  return dataUrl;
871
888
 
872
889
  return Object.assign(media, {
873
- mimetype: options.mimeType,
874
- data: dataUrl.replace(`data:${options.mimeType};base64,`, '')
890
+ mimetype: options.mimetype,
891
+ data: dataUrl.replace(`data:${options.mimetype};base64,`, '')
875
892
  });
876
893
  };
877
894
 
@@ -940,30 +957,14 @@ exports.LoadUtils = () => {
940
957
  return undefined;
941
958
  };
942
959
 
943
- window.WWebJS.getAddParticipantsRpcResult = async (groupMetadata, groupWid, participantWid) => {
944
- const participantLidArgs = groupMetadata?.isLidAddressingMode
945
- ? {
946
- phoneNumber: participantWid,
947
- lid: window.Store.LidUtils.getCurrentLid(participantWid)
948
- }
949
- : { phoneNumber: participantWid };
950
-
960
+ window.WWebJS.getAddParticipantsRpcResult = async (groupWid, participantWid) => {
951
961
  const iqTo = window.Store.WidToJid.widToGroupJid(groupWid);
952
962
 
953
- const participantArgs =
954
- participantLidArgs.lid
955
- ? [{
956
- participantJid: window.Store.WidToJid.widToUserJid(participantLidArgs.lid),
957
- phoneNumberMixinArgs: {
958
- anyPhoneNumber: window.Store.WidToJid.widToUserJid(participantLidArgs.phoneNumber)
959
- }
960
- }]
961
- : [{
962
- participantJid: window.Store.WidToJid.widToUserJid(participantLidArgs.phoneNumber)
963
- }];
963
+ const participantArgs = [{
964
+ participantJid: window.Store.WidToJid.widToUserJid(participantWid)
965
+ }];
964
966
 
965
967
  let rpcResult, resultArgs;
966
- const isOldImpl = window.compareWwebVersions(window.Debug.VERSION, '<=', '2.2335.9');
967
968
  const data = {
968
969
  name: undefined,
969
970
  code: undefined,
@@ -973,12 +974,10 @@ exports.LoadUtils = () => {
973
974
 
974
975
  try {
975
976
  rpcResult = await window.Store.GroupParticipants.sendAddParticipantsRPC({ participantArgs, iqTo });
976
- resultArgs = isOldImpl
977
- ? rpcResult.value.addParticipant[0].addParticipantsParticipantMixins
978
- : rpcResult.value.addParticipant[0]
979
- .addParticipantsParticipantAddedOrNonRegisteredWaUserParticipantErrorLidResponseMixinGroup
980
- .value
981
- .addParticipantsParticipantMixins;
977
+ resultArgs = rpcResult.value.addParticipant[0]
978
+ .addParticipantsParticipantAddedOrNonRegisteredWaUserParticipantErrorLidResponseMixinGroup
979
+ .value
980
+ .addParticipantsParticipantMixins;
982
981
  } catch (err) {
983
982
  data.code = 400;
984
983
  return data;
@@ -1122,7 +1121,16 @@ exports.LoadUtils = () => {
1122
1121
  window.WWebJS.pinUnpinMsgAction = async (msgId, action, duration) => {
1123
1122
  const message = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
1124
1123
  if (!message) return false;
1125
- const response = await window.Store.pinUnpinMsg(message, action, duration);
1124
+
1125
+ if (typeof duration !== 'number') return false;
1126
+
1127
+ const originalFunction = window.require('WAWebPinMsgConstants').getPinExpiryDuration;
1128
+ window.require('WAWebPinMsgConstants').getPinExpiryDuration = () => duration;
1129
+
1130
+ const response = await window.Store.PinnedMsgUtils.sendPinInChatMsg(message, action, duration);
1131
+
1132
+ window.require('WAWebPinMsgConstants').getPinExpiryDuration = originalFunction;
1133
+
1126
1134
  return response.messageSendResult === 'OK';
1127
1135
  };
1128
1136