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

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/src/Client.js CHANGED
@@ -51,6 +51,7 @@ const NoAuth = require('./authStrategies/NoAuth');
51
51
  * @fires Client#change_state
52
52
  * @fires Client#contact_changed
53
53
  * @fires Client#group_admin_changed
54
+ * @fires Client#group_membership_request
54
55
  */
55
56
  class Client extends EventEmitter {
56
57
  constructor(options = {}) {
@@ -172,7 +173,7 @@ class Client extends EventEmitter {
172
173
  }
173
174
  );
174
175
 
175
- const INTRO_IMG_SELECTOR = '[data-icon=\'chat\']';
176
+ const INTRO_IMG_SELECTOR = '[data-icon=\'search\']';
176
177
  const INTRO_QRCODE_SELECTOR = 'div[data-ref] canvas';
177
178
 
178
179
  // Checks which selector appears first
@@ -285,13 +286,13 @@ class Client extends EventEmitter {
285
286
  */
286
287
  window.compareWwebVersions = (lOperand, operator, rOperand) => {
287
288
  if (!['>', '>=', '<', '<=', '='].includes(operator)) {
288
- throw class _ extends Error {
289
+ throw new class _ extends Error {
289
290
  constructor(m) { super(m); this.name = 'CompareWwebVersionsError'; }
290
291
  }('Invalid comparison operator is provided');
291
292
 
292
293
  }
293
294
  if (typeof lOperand !== 'string' || typeof rOperand !== 'string') {
294
- throw class _ extends Error {
295
+ throw new class _ extends Error {
295
296
  constructor(m) { super(m); this.name = 'CompareWwebVersionsError'; }
296
297
  }('A non-string WWeb version type is provided');
297
298
  }
@@ -379,6 +380,17 @@ class Client extends EventEmitter {
379
380
  * @param {GroupNotification} notification GroupNotification with more information about the action
380
381
  */
381
382
  this.emit(Events.GROUP_ADMIN_CHANGED, notification);
383
+ } else if (msg.subtype === 'created_membership_requests') {
384
+ /**
385
+ * Emitted when some user requested to join the group
386
+ * that has the membership approval mode turned on
387
+ * @event Client#group_membership_request
388
+ * @param {GroupNotification} notification GroupNotification with more information about the action
389
+ * @param {string} notification.chatId The group ID the request was made for
390
+ * @param {string} notification.author The user ID that made a request
391
+ * @param {number} notification.timestamp The timestamp the request was made at
392
+ */
393
+ this.emit(Events.GROUP_MEMBERSHIP_REQUEST, notification);
382
394
  } else {
383
395
  /**
384
396
  * Emitted when group settings are updated, such as subject, description or picture.
@@ -975,7 +987,7 @@ class Client extends EventEmitter {
975
987
  */
976
988
  async getInviteInfo(inviteCode) {
977
989
  return await this.pupPage.evaluate(inviteCode => {
978
- return window.Store.InviteInfo.queryGroupInvite(inviteCode);
990
+ return window.Store.GroupInvite.queryGroupInvite(inviteCode);
979
991
  }, inviteCode);
980
992
  }
981
993
 
@@ -986,7 +998,7 @@ class Client extends EventEmitter {
986
998
  */
987
999
  async acceptInvite(inviteCode) {
988
1000
  const res = await this.pupPage.evaluate(async inviteCode => {
989
- return await window.Store.Invite.joinGroupViaInvite(inviteCode);
1001
+ return await window.Store.GroupInvite.joinGroupViaInvite(inviteCode);
990
1002
  }, inviteCode);
991
1003
 
992
1004
  return res.gid._serialized;
@@ -1003,7 +1015,7 @@ class Client extends EventEmitter {
1003
1015
  return this.pupPage.evaluate(async inviteInfo => {
1004
1016
  let { groupId, fromId, inviteCode, inviteCodeExp } = inviteInfo;
1005
1017
  let userWid = window.Store.WidFactory.createWid(fromId);
1006
- return await window.Store.JoinInviteV4.joinGroupViaInviteV4(inviteCode, String(inviteCodeExp), groupId, userWid);
1018
+ return await window.Store.GroupInviteV4.joinGroupViaInviteV4(inviteCode, String(inviteCodeExp), groupId, userWid);
1007
1019
  }, inviteInfo);
1008
1020
  }
1009
1021
 
@@ -1279,35 +1291,116 @@ class Client extends EventEmitter {
1279
1291
  }
1280
1292
 
1281
1293
  /**
1282
- * Create a new group
1283
- * @param {string} name group title
1284
- * @param {Array<Contact|string>} participants an array of Contacts or contact IDs to add to the group
1285
- * @returns {Object} createRes
1286
- * @returns {string} createRes.gid - ID for the group that was just created
1287
- * @returns {Object.<string,string>} createRes.missingParticipants - participants that were not added to the group. Keys represent the ID for participant that was not added and its value is a status code that represents the reason why participant could not be added. This is usually 403 if the user's privacy settings don't allow you to add them to groups.
1294
+ * An object that represents the result for a participant added to a group
1295
+ * @typedef {Object} ParticipantResult
1296
+ * @property {number} statusCode The status code of the result
1297
+ * @property {string} message The result message
1298
+ * @property {boolean} isGroupCreator Indicates if the participant is a group creator
1299
+ * @property {boolean} isInviteV4Sent Indicates if the inviteV4 was sent to the participant
1288
1300
  */
1289
- async createGroup(name, participants) {
1290
- if (!Array.isArray(participants) || participants.length == 0) {
1291
- throw 'You need to add at least one other participant to the group';
1292
- }
1293
1301
 
1294
- if (participants.every(c => c instanceof Contact)) {
1295
- participants = participants.map(c => c.id._serialized);
1296
- }
1302
+ /**
1303
+ * An object that handles the result for {@link createGroup} method
1304
+ * @typedef {Object} CreateGroupResult
1305
+ * @property {string} title A group title
1306
+ * @property {Object} gid An object that handles the newly created group ID
1307
+ * @property {string} gid.server
1308
+ * @property {string} gid.user
1309
+ * @property {string} gid._serialized
1310
+ * @property {Object.<string, ParticipantResult>} participants An object that handles the result value for each added to the group participant
1311
+ */
1312
+
1313
+ /**
1314
+ * An object that handles options for group creation
1315
+ * @typedef {Object} CreateGroupOptions
1316
+ * @property {number} [messageTimer = 0] The number of seconds for the messages to disappear in the group (0 by default, won't take an effect if the group is been creating with myself only)
1317
+ * @property {string|undefined} parentGroupId The ID of a parent community group to link the newly created group with (won't take an effect if the group is been creating with myself only)
1318
+ * @property {boolean} [autoSendInviteV4 = true] If true, the inviteV4 will be sent to those participants who have restricted others from being automatically added to groups, otherwise the inviteV4 won't be sent (true by default)
1319
+ * @property {string} [comment = ''] The comment to be added to an inviteV4 (empty string by default)
1320
+ */
1321
+
1322
+ /**
1323
+ * Creates a new group
1324
+ * @param {string} title Group title
1325
+ * @param {string|Contact|Array<Contact|string>|undefined} participants A single Contact object or an ID as a string or an array of Contact objects or contact IDs to add to the group
1326
+ * @param {CreateGroupOptions} options An object that handles options for group creation
1327
+ * @returns {Promise<CreateGroupResult|string>} Object with resulting data or an error message as a string
1328
+ */
1329
+ async createGroup(title, participants = [], options = {}) {
1330
+ !Array.isArray(participants) && (participants = [participants]);
1331
+ participants.map(p => (p instanceof Contact) ? p.id._serialized : p);
1332
+
1333
+ return await this.pupPage.evaluate(async (title, participants, options) => {
1334
+ const { messageTimer = 0, parentGroupId, autoSendInviteV4 = true, comment = '' } = options;
1335
+ const participantData = {}, participantWids = [], failedParticipants = [];
1336
+ let createGroupResult, parentGroupWid;
1337
+
1338
+ const addParticipantResultCodes = {
1339
+ default: 'An unknown error occupied while adding a participant',
1340
+ 200: 'The participant was added successfully',
1341
+ 403: 'The participant can be added by sending private invitation only',
1342
+ 404: 'The phone number is not registered on WhatsApp'
1343
+ };
1344
+
1345
+ for (const participant of participants) {
1346
+ const pWid = window.Store.WidFactory.createWid(participant);
1347
+ if ((await window.Store.QueryExist(pWid))?.wid) participantWids.push(pWid);
1348
+ else failedParticipants.push(participant);
1349
+ }
1350
+
1351
+ parentGroupId && (parentGroupWid = window.Store.WidFactory.createWid(parentGroupId));
1352
+
1353
+ try {
1354
+ createGroupResult = await window.Store.GroupUtils.createGroup(
1355
+ title,
1356
+ participantWids,
1357
+ messageTimer,
1358
+ parentGroupWid
1359
+ );
1360
+ } catch (err) {
1361
+ return 'CreateGroupError: An unknown error occupied while creating a group';
1362
+ }
1363
+
1364
+ for (const participant of createGroupResult.participants) {
1365
+ let isInviteV4Sent = false;
1366
+ const participantId = participant.wid._serialized;
1367
+ const statusCode = participant.error ?? 200;
1368
+
1369
+ if (autoSendInviteV4 && statusCode === 403) {
1370
+ window.Store.ContactCollection.gadd(participant.wid, { silent: true });
1371
+ const addParticipantResult = await window.Store.GroupInviteV4.sendGroupInviteMessage(
1372
+ await window.Store.Chat.find(participant.wid),
1373
+ createGroupResult.wid._serialized,
1374
+ createGroupResult.subject,
1375
+ participant.invite_code,
1376
+ participant.invite_code_exp,
1377
+ comment,
1378
+ await window.WWebJS.getProfilePicThumbToBase64(createGroupResult.wid)
1379
+ );
1380
+ isInviteV4Sent = window.compareWwebVersions(window.Debug.VERSION, '<', '2.2335.6')
1381
+ ? addParticipantResult === 'OK'
1382
+ : addParticipantResult.messageSendResult === 'OK';
1383
+ }
1297
1384
 
1298
- const createRes = await this.pupPage.evaluate(async (name, participantIds) => {
1299
- const participantWIDs = participantIds.map(p => window.Store.WidFactory.createWid(p));
1300
- return await window.Store.GroupUtils.createGroup(name, participantWIDs, 0);
1301
- }, name, participants);
1385
+ participantData[participantId] = {
1386
+ statusCode: statusCode,
1387
+ message: addParticipantResultCodes[statusCode] || addParticipantResultCodes.default,
1388
+ isGroupCreator: participant.type === 'superadmin',
1389
+ isInviteV4Sent: isInviteV4Sent
1390
+ };
1391
+ }
1302
1392
 
1303
- const missingParticipants = createRes.participants.reduce(((missing, c) => {
1304
- const id = c.wid._serialized;
1305
- const statusCode = c.error ? c.error.toString() : '200';
1306
- if (statusCode != 200) return Object.assign(missing, { [id]: statusCode });
1307
- return missing;
1308
- }), {});
1393
+ for (const f of failedParticipants) {
1394
+ participantData[f] = {
1395
+ statusCode: 404,
1396
+ message: addParticipantResultCodes[404],
1397
+ isGroupCreator: false,
1398
+ isInviteV4Sent: false
1399
+ };
1400
+ }
1309
1401
 
1310
- return { gid: createRes.wid, missingParticipants };
1402
+ return { title: title, gid: createGroupResult.wid, participants: participantData };
1403
+ }, title, participants, options);
1311
1404
  }
1312
1405
 
1313
1406
  /**
@@ -1434,6 +1527,69 @@ class Client extends EventEmitter {
1434
1527
  return await window.Store.Label.addOrRemoveLabels(actions, chats);
1435
1528
  }, labelIds, chatIds);
1436
1529
  }
1530
+
1531
+ /**
1532
+ * An object that handles the information about the group membership request
1533
+ * @typedef {Object} GroupMembershipRequest
1534
+ * @property {Object} id The wid of a user who requests to enter the group
1535
+ * @property {Object} addedBy The wid of a user who created that request
1536
+ * @property {Object|null} parentGroupId The wid of a community parent group to which the current group is linked
1537
+ * @property {string} requestMethod The method used to create the request: NonAdminAdd/InviteLink/LinkedGroupJoin
1538
+ * @property {number} t The timestamp the request was created at
1539
+ */
1540
+
1541
+ /**
1542
+ * Gets an array of membership requests
1543
+ * @param {string} groupId The ID of a group to get membership requests for
1544
+ * @returns {Promise<Array<GroupMembershipRequest>>} An array of membership requests
1545
+ */
1546
+ async getGroupMembershipRequests(groupId) {
1547
+ return await this.pupPage.evaluate(async (gropId) => {
1548
+ const groupWid = window.Store.WidFactory.createWid(gropId);
1549
+ return await window.Store.MembershipRequestUtils.getMembershipApprovalRequests(groupWid);
1550
+ }, groupId);
1551
+ }
1552
+
1553
+ /**
1554
+ * An object that handles the result for membership request action
1555
+ * @typedef {Object} MembershipRequestActionResult
1556
+ * @property {string} requesterId User ID whos membership request was approved/rejected
1557
+ * @property {number|undefined} error An error code that occurred during the operation for the participant
1558
+ * @property {string} message A message with a result of membership request action
1559
+ */
1560
+
1561
+ /**
1562
+ * An object that handles options for {@link approveGroupMembershipRequests} and {@link rejectGroupMembershipRequests} methods
1563
+ * @typedef {Object} MembershipRequestActionOptions
1564
+ * @property {Array<string>|string|null} requesterIds User ID/s who requested to join the group, if no value is provided, the method will search for all membership requests for that group
1565
+ * @property {Array<number>|number|null} sleep The number of milliseconds to wait before performing an operation for the next requester. If it is an array, a random sleep time between the sleep[0] and sleep[1] values will be added (the difference must be >=100 ms, otherwise, a random sleep time between sleep[1] and sleep[1] + 100 will be added). If sleep is a number, a sleep time equal to its value will be added. By default, sleep is an array with a value of [250, 500]
1566
+ */
1567
+
1568
+ /**
1569
+ * Approves membership requests if any
1570
+ * @param {string} groupId The group ID to get the membership request for
1571
+ * @param {MembershipRequestActionOptions} options Options for performing a membership request action
1572
+ * @returns {Promise<Array<MembershipRequestActionResult>>} Returns an array of requester IDs whose membership requests were approved and an error for each requester, if any occurred during the operation. If there are no requests, an empty array will be returned
1573
+ */
1574
+ async approveGroupMembershipRequests(groupId, options = {}) {
1575
+ return await this.pupPage.evaluate(async (groupId, options) => {
1576
+ const { requesterIds = null, sleep = [250, 500] } = options;
1577
+ return await window.WWebJS.membershipRequestAction(groupId, 'Approve', requesterIds, sleep);
1578
+ }, groupId, options);
1579
+ }
1580
+
1581
+ /**
1582
+ * Rejects membership requests if any
1583
+ * @param {string} groupId The group ID to get the membership request for
1584
+ * @param {MembershipRequestActionOptions} options Options for performing a membership request action
1585
+ * @returns {Promise<Array<MembershipRequestActionResult>>} Returns an array of requester IDs whose membership requests were rejected and an error for each requester, if any occurred during the operation. If there are no requests, an empty array will be returned
1586
+ */
1587
+ async rejectGroupMembershipRequests(groupId, options = {}) {
1588
+ return await this.pupPage.evaluate(async (groupId, options) => {
1589
+ const { requesterIds = null, sleep = [250, 500] } = options;
1590
+ return await window.WWebJS.membershipRequestAction(groupId, 'Reject', requesterIds, sleep);
1591
+ }, groupId, options);
1592
+ }
1437
1593
  }
1438
1594
 
1439
1595
  module.exports = Client;
@@ -53,28 +53,141 @@ class GroupChat extends Chat {
53
53
  return this.groupMetadata.participants;
54
54
  }
55
55
 
56
+ /**
57
+ * An object that handles the result for {@link addParticipants} method
58
+ * @typedef {Object} AddParticipantsResult
59
+ * @property {number} code The code of the result
60
+ * @property {string} message The result message
61
+ * @property {boolean} isInviteV4Sent Indicates if the inviteV4 was sent to the partitipant
62
+ */
63
+
64
+ /**
65
+ * An object that handles options for adding participants
66
+ * @typedef {Object} AddParticipnatsOptions
67
+ * @property {Array<number>|number} [sleep = [250, 500]] The number of milliseconds to wait before adding the next participant. If it is an array, a random sleep time between the sleep[0] and sleep[1] values will be added (the difference must be >=100 ms, otherwise, a random sleep time between sleep[1] and sleep[1] + 100 will be added). If sleep is a number, a sleep time equal to its value will be added. By default, sleep is an array with a value of [250, 500]
68
+ * @property {boolean} [autoSendInviteV4 = true] If true, the inviteV4 will be sent to those participants who have restricted others from being automatically added to groups, otherwise the inviteV4 won't be sent (true by default)
69
+ * @property {string} [comment = ''] The comment to be added to an inviteV4 (empty string by default)
70
+ */
71
+
56
72
  /**
57
73
  * Adds a list of participants by ID to the group
58
- * @param {Array<string>} participantIds
59
- * @returns {Promise<Object>}
74
+ * @param {string|Array<string>} participantIds
75
+ * @param {AddParticipnatsOptions} options An object thay handles options for adding participants
76
+ * @returns {Promise<Object.<string, AddParticipantsResult>|string>} Returns an object with the resulting data or an error message as a string
60
77
  */
61
- async addParticipants(participantIds) {
62
- return await this.client.pupPage.evaluate(async (chatId, participantIds) => {
63
- const chatWid = window.Store.WidFactory.createWid(chatId);
64
- const chat = await window.Store.Chat.find(chatWid);
65
- const participants = await Promise.all(participantIds.map(async p => {
66
- const wid = window.Store.WidFactory.createWid(p);
67
- return await window.Store.Contact.get(wid);
68
- }));
69
- await window.Store.GroupParticipants.addParticipants(chat, participants);
70
- return { status: 200 };
71
- }, this.id._serialized, participantIds);
78
+ async addParticipants(participantIds, options = {}) {
79
+ return await this.client.pupPage.evaluate(async (groupId, participantIds, options) => {
80
+ const { sleep = [250, 500], autoSendInviteV4 = true, comment = '' } = options;
81
+ const participantData = {};
82
+
83
+ !Array.isArray(participantIds) && (participantIds = [participantIds]);
84
+ const groupWid = window.Store.WidFactory.createWid(groupId);
85
+ const group = await window.Store.Chat.find(groupWid);
86
+ const participantWids = participantIds.map((p) => window.Store.WidFactory.createWid(p));
87
+
88
+ const errorCodes = {
89
+ default: 'An unknown error occupied while adding a participant',
90
+ isGroupEmpty: 'AddParticipantsError: The participant can\'t be added to an empty group',
91
+ iAmNotAdmin: 'AddParticipantsError: You have no admin rights to add a participant to a group',
92
+ 200: 'The participant was added successfully',
93
+ 403: 'The participant can be added by sending private invitation only',
94
+ 404: 'The phone number is not registered on WhatsApp',
95
+ 408: 'You cannot add this participant because they recently left the group',
96
+ 409: 'The participant is already a group member',
97
+ 417: 'The participant can\'t be added to the community. You can invite them privately to join this group through its invite link',
98
+ 419: 'The participant can\'t be added because the group is full'
99
+ };
100
+
101
+ await window.Store.GroupMetadata.queryAndUpdate(groupWid);
102
+ const groupMetadata = group.groupMetadata;
103
+ const groupParticipants = groupMetadata?.participants;
104
+
105
+ if (!groupParticipants) {
106
+ return errorCodes.isGroupEmpty;
107
+ }
108
+
109
+ if (!group.iAmAdmin()) {
110
+ return errorCodes.iAmNotAdmin;
111
+ }
112
+
113
+ const _getSleepTime = (sleep) => {
114
+ if (!Array.isArray(sleep) || sleep.length === 2 && sleep[0] === sleep[1]) {
115
+ return sleep;
116
+ }
117
+ if (sleep.length === 1) {
118
+ return sleep[0];
119
+ }
120
+ (sleep[1] - sleep[0]) < 100 && (sleep[0] = sleep[1]) && (sleep[1] += 100);
121
+ return Math.floor(Math.random() * (sleep[1] - sleep[0] + 1)) + sleep[0];
122
+ };
123
+
124
+ for (const pWid of participantWids) {
125
+ const pId = pWid._serialized;
126
+
127
+ participantData[pId] = {
128
+ code: undefined,
129
+ message: undefined,
130
+ isInviteV4Sent: false
131
+ };
132
+
133
+ if (groupParticipants.some(p => p.id._serialized === pId)) {
134
+ participantData[pId].code = 409;
135
+ participantData[pId].message = errorCodes[409];
136
+ continue;
137
+ }
138
+
139
+ if (!(await window.Store.QueryExist(pWid))?.wid) {
140
+ participantData[pId].code = 404;
141
+ participantData[pId].message = errorCodes[404];
142
+ continue;
143
+ }
144
+
145
+ const rpcResult =
146
+ await window.WWebJS.getAddParticipantsRpcResult(groupMetadata, groupWid, pWid);
147
+ const { code: rpcResultCode } = rpcResult;
148
+
149
+ participantData[pId].code = rpcResultCode;
150
+ participantData[pId].message =
151
+ errorCodes[rpcResultCode] || errorCodes.default;
152
+
153
+ if (autoSendInviteV4 && rpcResultCode === 403) {
154
+ let userChat, isInviteV4Sent = false;
155
+ window.Store.ContactCollection.gadd(pWid, { silent: true });
156
+
157
+ if (rpcResult.name === 'ParticipantRequestCodeCanBeSent' &&
158
+ (userChat = await window.Store.Chat.find(pWid))) {
159
+ const groupName = group.formattedTitle || group.name;
160
+ const res = await window.Store.GroupInviteV4.sendGroupInviteMessage(
161
+ userChat,
162
+ group.id._serialized,
163
+ groupName,
164
+ rpcResult.inviteV4Code,
165
+ rpcResult.inviteV4CodeExp,
166
+ comment,
167
+ await window.WWebJS.getProfilePicThumbToBase64(groupWid)
168
+ );
169
+ isInviteV4Sent = window.compareWwebVersions(window.Debug.VERSION, '<', '2.2335.6')
170
+ ? res === 'OK'
171
+ : res.messageSendResult === 'OK';
172
+ }
173
+
174
+ participantData[pId].isInviteV4Sent = isInviteV4Sent;
175
+ }
176
+
177
+ sleep &&
178
+ participantWids.length > 1 &&
179
+ participantWids.indexOf(pWid) !== participantWids.length - 1 &&
180
+ (await new Promise((resolve) => setTimeout(resolve, _getSleepTime(sleep))));
181
+ }
182
+
183
+ return participantData;
184
+ }, this.id._serialized, participantIds, options);
72
185
  }
73
186
 
74
187
  /**
75
188
  * Removes a list of participants by ID to the group
76
189
  * @param {Array<string>} participantIds
77
- * @returns {Promise<Object>}
190
+ * @returns {Promise<{ status: number }>}
78
191
  */
79
192
  async removeParticipants(participantIds) {
80
193
  return await this.client.pupPage.evaluate(async (chatId, participantIds) => {
@@ -246,7 +359,7 @@ class GroupChat extends Chat {
246
359
  async getInviteCode() {
247
360
  const codeRes = await this.client.pupPage.evaluate(async chatId => {
248
361
  const chatWid = window.Store.WidFactory.createWid(chatId);
249
- return window.Store.Invite.queryGroupInviteCode(chatWid);
362
+ return window.Store.GroupInvite.queryGroupInviteCode(chatWid);
250
363
  }, this.id._serialized);
251
364
 
252
365
  return codeRes.code;
@@ -259,11 +372,62 @@ class GroupChat extends Chat {
259
372
  async revokeInvite() {
260
373
  const codeRes = await this.client.pupPage.evaluate(chatId => {
261
374
  const chatWid = window.Store.WidFactory.createWid(chatId);
262
- return window.Store.Invite.resetGroupInviteCode(chatWid);
375
+ return window.Store.GroupInvite.resetGroupInviteCode(chatWid);
263
376
  }, this.id._serialized);
264
377
 
265
378
  return codeRes.code;
266
379
  }
380
+
381
+ /**
382
+ * An object that handles the information about the group membership request
383
+ * @typedef {Object} GroupMembershipRequest
384
+ * @property {Object} id The wid of a user who requests to enter the group
385
+ * @property {Object} addedBy The wid of a user who created that request
386
+ * @property {Object|null} parentGroupId The wid of a community parent group to which the current group is linked
387
+ * @property {string} requestMethod The method used to create the request: NonAdminAdd/InviteLink/LinkedGroupJoin
388
+ * @property {number} t The timestamp the request was created at
389
+ */
390
+
391
+ /**
392
+ * Gets an array of membership requests
393
+ * @returns {Promise<Array<GroupMembershipRequest>>} An array of membership requests
394
+ */
395
+ async getGroupMembershipRequests() {
396
+ return await this.client.getGroupMembershipRequests(this.id._serialized);
397
+ }
398
+
399
+ /**
400
+ * An object that handles the result for membership request action
401
+ * @typedef {Object} MembershipRequestActionResult
402
+ * @property {string} requesterId User ID whos membership request was approved/rejected
403
+ * @property {number} error An error code that occurred during the operation for the participant
404
+ * @property {string} message A message with a result of membership request action
405
+ */
406
+
407
+ /**
408
+ * An object that handles options for {@link approveGroupMembershipRequests} and {@link rejectGroupMembershipRequests} methods
409
+ * @typedef {Object} MembershipRequestActionOptions
410
+ * @property {Array<string>|string|null} requesterIds User ID/s who requested to join the group, if no value is provided, the method will search for all membership requests for that group
411
+ * @property {Array<number>|number|null} sleep The number of milliseconds to wait before performing an operation for the next requester. If it is an array, a random sleep time between the sleep[0] and sleep[1] values will be added (the difference must be >=100 ms, otherwise, a random sleep time between sleep[1] and sleep[1] + 100 will be added). If sleep is a number, a sleep time equal to its value will be added. By default, sleep is an array with a value of [250, 500]
412
+ */
413
+
414
+ /**
415
+ * Approves membership requests if any
416
+ * @param {MembershipRequestActionOptions} options Options for performing a membership request action
417
+ * @returns {Promise<Array<MembershipRequestActionResult>>} Returns an array of requester IDs whose membership requests were approved and an error for each requester, if any occurred during the operation. If there are no requests, an empty array will be returned
418
+ */
419
+ async approveGroupMembershipRequests(options = {}) {
420
+ return await this.client.approveGroupMembershipRequests(this.id._serialized, options);
421
+ }
422
+
423
+ /**
424
+ * Rejects membership requests if any
425
+ * @param {MembershipRequestActionOptions} options Options for performing a membership request action
426
+ * @returns {Promise<Array<MembershipRequestActionResult>>} Returns an array of requester IDs whose membership requests were rejected and an error for each requester, if any occurred during the operation. If there are no requests, an empty array will be returned
427
+ */
428
+ async rejectGroupMembershipRequests(options = {}) {
429
+ return await this.client.rejectGroupMembershipRequests(this.id._serialized, options);
430
+ }
267
431
 
268
432
  /**
269
433
  * Makes the bot leave the group
@@ -1,5 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ /**
4
+ * Location send options
5
+ * @typedef {Object} LocationSendOptions
6
+ * @property {string} [name] Location name
7
+ * @property {string} [address] Location address
8
+ * @property {string} [url] URL address to be shown within a location message
9
+ */
10
+
3
11
  /**
4
12
  * Location information
5
13
  */
@@ -7,9 +15,9 @@ class Location {
7
15
  /**
8
16
  * @param {number} latitude
9
17
  * @param {number} longitude
10
- * @param {?string} description
18
+ * @param {LocationSendOptions} [options] Location send options
11
19
  */
12
- constructor(latitude, longitude, description) {
20
+ constructor(latitude, longitude, options = {}) {
13
21
  /**
14
22
  * Location latitude
15
23
  * @type {number}
@@ -24,9 +32,29 @@ class Location {
24
32
 
25
33
  /**
26
34
  * Name for the location
27
- * @type {?string}
35
+ * @type {string|undefined}
36
+ */
37
+ this.name = options.name;
38
+
39
+ /**
40
+ * Location address
41
+ * @type {string|undefined}
42
+ */
43
+ this.address = options.address;
44
+
45
+ /**
46
+ * URL address to be shown within a location message
47
+ * @type {string|undefined}
48
+ */
49
+ this.url = options.url;
50
+
51
+ /**
52
+ * Location full description
53
+ * @type {string|undefined}
28
54
  */
29
- this.description = description;
55
+ this.description = this.name && this.address
56
+ ? `${this.name}\n${this.address}`
57
+ : this.name || this.address || '';
30
58
  }
31
59
  }
32
60
 
@@ -151,7 +151,21 @@ class Message extends Base {
151
151
  * Location information contained in the message, if the message is type "location"
152
152
  * @type {Location}
153
153
  */
154
- this.location = data.type === MessageTypes.LOCATION ? new Location(data.lat, data.lng, data.loc) : undefined;
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
+ })();
155
169
 
156
170
  /**
157
171
  * List of vCards contained in the message.
@@ -56,6 +56,7 @@ exports.Events = {
56
56
  GROUP_JOIN: 'group_join',
57
57
  GROUP_LEAVE: 'group_leave',
58
58
  GROUP_ADMIN_CHANGED: 'group_admin_changed',
59
+ GROUP_MEMBERSHIP_REQUEST: 'group_membership_request',
59
60
  GROUP_UPDATE: 'group_update',
60
61
  QR_RECEIVED: 'qr',
61
62
  LOADING_SCREEN: 'loading_screen',