whatsapp-web.js 1.22.2-alpha.2 → 1.22.2-alpha.4

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
@@ -1,4 +1,4 @@
1
- const { Client, Location, List, Buttons, LocalAuth } = require('./index');
1
+ const { Client, Location, Poll, List, Buttons, LocalAuth } = require('./index');
2
2
 
3
3
  const client = new Client({
4
4
  authStrategy: new LocalAuth(),
@@ -290,6 +290,22 @@ client.on('message', async msg => {
290
290
  client.sendMessage(msg.from, list);
291
291
  } else if (msg.body === '!reaction') {
292
292
  msg.react('👍');
293
+ } else if (msg.body === '!sendpoll') {
294
+ /** By default the poll is created as a single choice poll: */
295
+ await msg.reply(new Poll('Winter or Summer?', ['Winter', 'Summer']));
296
+ /** If you want to provide a multiple choice poll, add allowMultipleAnswers as true: */
297
+ await msg.reply(new Poll('Cats or Dogs?', ['Cats', 'Dogs'], { allowMultipleAnswers: true }));
298
+ /**
299
+ * You can provide a custom message secret, it can be used as a poll ID:
300
+ * @note It has to be a unique vector with a length of 32
301
+ */
302
+ await msg.reply(
303
+ new Poll('Cats or Dogs?', ['Cats', 'Dogs'], {
304
+ messageSecret: [
305
+ 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
306
+ ]
307
+ })
308
+ );
293
309
  } else if (msg.body === '!edit') {
294
310
  if (msg.hasQuotedMsg) {
295
311
  const quotedMsg = await msg.getQuotedMessage();
package/index.d.ts CHANGED
@@ -707,6 +707,7 @@ declare namespace WAWebJS {
707
707
  PROTOCOL = 'protocol',
708
708
  REACTION = 'reaction',
709
709
  TEMPLATE_BUTTON_REPLY = 'template_button_reply',
710
+ POLL_CREATION = 'poll_creation',
710
711
  }
711
712
 
712
713
  /** Client status */
@@ -866,6 +867,11 @@ declare namespace WAWebJS {
866
867
  selectedRowId?: string,
867
868
  /** Returns message in a raw format */
868
869
  rawData: object,
870
+ pollName: string,
871
+ /** Avaiaible poll voting options */
872
+ pollOptions: string[],
873
+ /** False for a single choice poll, true for a multiple choice poll */
874
+ allowMultipleAnswers: boolean,
869
875
  /*
870
876
  * Reloads this Message object's data in-place with the latest values from WhatsApp Web.
871
877
  * Note that the Message must still be in the web app cache for this to work, otherwise will return null.
@@ -946,6 +952,27 @@ declare namespace WAWebJS {
946
952
  constructor(latitude: number, longitude: number, options?: LocationSendOptions)
947
953
  }
948
954
 
955
+ /** Poll send options */
956
+ export interface PollSendOptions {
957
+ /** False for a single choice poll, true for a multiple choice poll (false by default) */
958
+ allowMultipleAnswers?: boolean,
959
+ /**
960
+ * The custom message secret, can be used as a poll ID
961
+ * @note It has to be a unique vector with a length of 32
962
+ */
963
+ messageSecret: ?Array<number>
964
+ }
965
+
966
+ /** Represents a Poll on WhatsApp */
967
+ export interface Poll {
968
+ pollName: string,
969
+ pollOptions: Array<{
970
+ name: string,
971
+ localId: number
972
+ }>,
973
+ options: PollSendOptions
974
+ }
975
+
949
976
  export interface Label {
950
977
  /** Label name */
951
978
  name: string,
@@ -1037,7 +1064,7 @@ declare namespace WAWebJS {
1037
1064
  static fromUrl: (url: string, options?: MediaFromURLOptions) => Promise<MessageMedia>
1038
1065
  }
1039
1066
 
1040
- export type MessageContent = string | MessageMedia | Location | Contact | Contact[] | List | Buttons
1067
+ export type MessageContent = string | MessageMedia | Location | Poll | Contact | Contact[] | List | Buttons
1041
1068
 
1042
1069
  /**
1043
1070
  * Represents a Contact on WhatsApp
package/index.js CHANGED
@@ -18,6 +18,7 @@ module.exports = {
18
18
  BusinessContact: require('./src/structures/BusinessContact'),
19
19
  ClientInfo: require('./src/structures/ClientInfo'),
20
20
  Location: require('./src/structures/Location'),
21
+ Poll: require('./src/structures/Poll'),
21
22
  ProductMetadata: require('./src/structures/ProductMetadata'),
22
23
  List: require('./src/structures/List'),
23
24
  Buttons: require('./src/structures/Buttons'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whatsapp-web.js",
3
- "version": "1.22.2-alpha.2",
3
+ "version": "1.22.2-alpha.4",
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
@@ -11,7 +11,7 @@ const { ExposeStore, LoadUtils } = require('./util/Injected');
11
11
  const ChatFactory = require('./factories/ChatFactory');
12
12
  const ContactFactory = require('./factories/ContactFactory');
13
13
  const WebCacheFactory = require('./webCache/WebCacheFactory');
14
- const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List, Reaction, Chat } = require('./structures');
14
+ const { ClientInfo, Message, MessageMedia, Contact, Location, Poll, GroupNotification, Label, Call, Buttons, List, Reaction } = require('./structures');
15
15
  const LegacySessionAuth = require('./authStrategies/LegacySessionAuth');
16
16
  const NoAuth = require('./authStrategies/NoAuth');
17
17
 
@@ -620,16 +620,20 @@ class Client extends EventEmitter {
620
620
  }
621
621
  });
622
622
 
623
- await page.exposeFunction('onRemoveChatEvent', (chat) => {
623
+ await page.exposeFunction('onRemoveChatEvent', async (chat) => {
624
+ const _chat = await this.getChatById(chat.id);
625
+
624
626
  /**
625
627
  * Emitted when a chat is removed
626
628
  * @event Client#chat_removed
627
629
  * @param {Chat} chat
628
630
  */
629
- this.emit(Events.CHAT_REMOVED, new Chat(this, chat));
631
+ this.emit(Events.CHAT_REMOVED, _chat);
630
632
  });
631
633
 
632
- await page.exposeFunction('onArchiveChatEvent', (chat, currState, prevState) => {
634
+ await page.exposeFunction('onArchiveChatEvent', async (chat, currState, prevState) => {
635
+ const _chat = await this.getChatById(chat.id);
636
+
633
637
  /**
634
638
  * Emitted when a chat is archived/unarchived
635
639
  * @event Client#chat_archived
@@ -637,7 +641,7 @@ class Client extends EventEmitter {
637
641
  * @param {boolean} currState
638
642
  * @param {boolean} prevState
639
643
  */
640
- this.emit(Events.CHAT_ARCHIVED, new Chat(this, chat), currState, prevState);
644
+ this.emit(Events.CHAT_ARCHIVED, _chat, currState, prevState);
641
645
  });
642
646
 
643
647
  await page.exposeFunction('onEditMessageEvent', (msg, newBody, prevBody) => {
@@ -816,7 +820,7 @@ class Client extends EventEmitter {
816
820
  /**
817
821
  * Send a message to a specific chatId
818
822
  * @param {string} chatId
819
- * @param {string|MessageMedia|Location|Contact|Array<Contact>|Buttons|List} content
823
+ * @param {string|MessageMedia|Location|Poll|Contact|Array<Contact>|Buttons|List} content
820
824
  * @param {MessageSendOptions} [options] - Options used when sending the message
821
825
  *
822
826
  * @returns {Promise<Message>} Message that was just sent
@@ -853,6 +857,9 @@ class Client extends EventEmitter {
853
857
  } else if (content instanceof Location) {
854
858
  internalOptions.location = content;
855
859
  content = '';
860
+ } else if (content instanceof Poll) {
861
+ internalOptions.poll = content;
862
+ content = '';
856
863
  } else if (content instanceof Contact) {
857
864
  internalOptions.contactCard = content.id._serialized;
858
865
  content = '';
@@ -51,7 +51,7 @@ class Message extends Base {
51
51
  * Message content
52
52
  * @type {string}
53
53
  */
54
- this.body = this.hasMedia ? data.caption || '' : data.body || '';
54
+ this.body = this.hasMedia ? data.caption || '' : data.body || data.pollName || '';
55
55
 
56
56
  /**
57
57
  * Message type
@@ -271,6 +271,20 @@ class Message extends Base {
271
271
  this.selectedRowId = data.listResponse.singleSelectReply.selectedRowId;
272
272
  }
273
273
 
274
+ if (this.type === MessageTypes.POLL_CREATION) {
275
+ this.pollName = data.pollName;
276
+ this.pollOptions = data.pollOptions;
277
+ this.allowMultipleAnswers = Boolean(!data.pollSelectableOptionsCount);
278
+ this.pollInvalidated = data.pollInvalidated;
279
+ this.isSentCagPollCreation = data.isSentCagPollCreation;
280
+
281
+ delete this._data.pollName;
282
+ delete this._data.pollOptions;
283
+ delete this._data.pollSelectableOptionsCount;
284
+ delete this._data.pollInvalidated;
285
+ delete this._data.isSentCagPollCreation;
286
+ }
287
+
274
288
  return super._patch(data);
275
289
  }
276
290
 
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Poll send options
5
+ * @typedef {Object} PollSendOptions
6
+ * @property {boolean} [allowMultipleAnswers=false] If false it is a single choice poll, otherwise it is a multiple choice poll (false by default)
7
+ * @property {?Array<number>} messageSecret The custom message secret, can be used as a poll ID. NOTE: it has to be a unique vector with a length of 32
8
+ */
9
+
10
+ /** Represents a Poll on WhatsApp */
11
+ class Poll {
12
+ /**
13
+ * @param {string} pollName
14
+ * @param {Array<string>} pollOptions
15
+ * @param {PollSendOptions} options
16
+ */
17
+ constructor(pollName, pollOptions, options = {}) {
18
+ /**
19
+ * The name of the poll
20
+ * @type {string}
21
+ */
22
+ this.pollName = pollName.trim();
23
+
24
+ /**
25
+ * The array of poll options
26
+ * @type {Array.<{name: string, localId: number}>}
27
+ */
28
+ this.pollOptions = pollOptions.map((option, index) => ({
29
+ name: option.trim(),
30
+ localId: index
31
+ }));
32
+
33
+ /**
34
+ * The send options for the poll
35
+ * @type {PollSendOptions}
36
+ */
37
+ this.options = {
38
+ allowMultipleAnswers: options.allowMultipleAnswers === true,
39
+ messageSecret: options.messageSecret
40
+ };
41
+ }
42
+ }
43
+
44
+ module.exports = Poll;
@@ -19,4 +19,5 @@ module.exports = {
19
19
  List: require('./List'),
20
20
  Payment: require('./Payment'),
21
21
  Reaction: require('./Reaction'),
22
+ Poll: require('./Poll'),
22
23
  };
@@ -108,6 +108,7 @@ exports.MessageTypes = {
108
108
  PROTOCOL: 'protocol',
109
109
  REACTION: 'reaction',
110
110
  TEMPLATE_BUTTON_REPLY: 'template_button_reply',
111
+ POLL_CREATION: 'poll_creation',
111
112
  };
112
113
 
113
114
  /**
@@ -62,7 +62,6 @@ exports.ExposeStore = (moduleRaidStr) => {
62
62
  /* eslint-disable no-undef, no-cond-assign */
63
63
  window.Store.QueryExist = ((m = window.mR.findModule('queryExists')[0]) ? m.queryExists : window.mR.findModule('queryExist')[0].queryWidExists);
64
64
  window.Store.ReplyUtils = (m = window.mR.findModule('canReplyMsg')).length > 0 && m[0];
65
- if ((m = window.mR.findModule('ChatCollection')[0]) && m.ChatCollection && typeof m.ChatCollection.findImpl === 'undefined' && typeof m.ChatCollection._find !== 'undefined') m.ChatCollection.findImpl = m.ChatCollection._find;
66
65
  /* eslint-enable no-undef, no-cond-assign */
67
66
 
68
67
  window.Store.StickerTools = {
@@ -100,6 +99,9 @@ exports.ExposeStore = (moduleRaidStr) => {
100
99
  });
101
100
  };
102
101
  }
102
+
103
+ // eslint-disable-next-line no-undef
104
+ if ((m = window.mR.findModule('ChatCollection')[0]) && m.ChatCollection && typeof m.ChatCollection.findImpl === 'undefined' && typeof m.ChatCollection._find !== 'undefined') m.ChatCollection.findImpl = m.ChatCollection._find;
103
105
 
104
106
  // TODO remove these once everybody has been updated to WWebJS with legacy sessions removed
105
107
  const _linkPreview = window.mR.findModule('queryLinkPreview');
@@ -140,7 +142,7 @@ exports.ExposeStore = (moduleRaidStr) => {
140
142
  const modifiedFunction = (...args) => callback(originalFunction, ...args);
141
143
  module[target.index][target.property] = modifiedFunction;
142
144
  };
143
-
145
+
144
146
  window.injectToFunction({ moduleId: 'mediaTypeFromProtobuf', index: 0, property: 'mediaTypeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage ? null : func(...args); });
145
147
 
146
148
  window.injectToFunction({ moduleId: 'typeAttributeFromProtobuf', index: 0, property: 'typeAttributeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage || proto.groupInviteMessage ? 'text' : func(...args); });
@@ -213,6 +215,23 @@ exports.LoadUtils = () => {
213
215
  delete options.location;
214
216
  }
215
217
 
218
+ let _pollOptions = {};
219
+ if (options.poll) {
220
+ const { pollName, pollOptions } = options.poll;
221
+ const { allowMultipleAnswers, messageSecret } = options.poll.options;
222
+ _pollOptions = {
223
+ type: 'poll_creation',
224
+ pollName: pollName,
225
+ pollOptions: pollOptions,
226
+ pollSelectableOptionsCount: allowMultipleAnswers ? 0 : 1,
227
+ messageSecret:
228
+ Array.isArray(messageSecret) && messageSecret.length === 32
229
+ ? new Uint8Array(messageSecret)
230
+ : window.crypto.getRandomValues(new Uint8Array(32))
231
+ };
232
+ delete options.poll;
233
+ }
234
+
216
235
  let vcardOptions = {};
217
236
  if (options.contactCard) {
218
237
  let contact = window.Store.Contact.get(options.contactCard);
@@ -332,6 +351,7 @@ exports.LoadUtils = () => {
332
351
  type: 'chat',
333
352
  ...ephemeralFields,
334
353
  ...locationOptions,
354
+ ..._pollOptions,
335
355
  ...attOptions,
336
356
  ...(attOptions.toJSON ? attOptions.toJSON() : {}),
337
357
  ...quotedMsgOptions,