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.
- package/example.js +20 -10
- package/index.d.ts +139 -6
- package/index.js +1 -0
- package/package.json +1 -1
- package/src/Client.js +240 -62
- package/src/structures/Chat.js +8 -0
- package/src/structures/GroupChat.js +26 -14
- package/src/structures/Message.js +44 -1
- package/src/structures/PollVote.js +20 -6
- package/src/structures/ScheduledEvent.js +71 -0
- package/src/structures/index.js +2 -1
- package/src/util/Constants.js +8 -1
- package/src/util/Injected/Store.js +10 -2
- package/src/util/Injected/Utils.js +58 -50
package/src/Client.js
CHANGED
|
@@ -15,7 +15,7 @@ const { LoadUtils } = require('./util/Injected/Utils');
|
|
|
15
15
|
const ChatFactory = require('./factories/ChatFactory');
|
|
16
16
|
const ContactFactory = require('./factories/ContactFactory');
|
|
17
17
|
const WebCacheFactory = require('./webCache/WebCacheFactory');
|
|
18
|
-
const {
|
|
18
|
+
const { ClientInfo, Message, MessageMedia, Contact, Location, Poll, PollVote, GroupNotification, Label, Call, Buttons, List, Reaction, Broadcast, ScheduledEvent } = require('./structures');
|
|
19
19
|
const NoAuth = require('./authStrategies/NoAuth');
|
|
20
20
|
const {exposeFunctionIfAbsent} = require('./util/Puppeteer');
|
|
21
21
|
|
|
@@ -36,6 +36,8 @@ const {exposeFunctionIfAbsent} = require('./util/Puppeteer');
|
|
|
36
36
|
* @param {string} options.userAgent - User agent to use in puppeteer
|
|
37
37
|
* @param {string} options.ffmpegPath - Ffmpeg path to use when formatting videos to webp while sending stickers
|
|
38
38
|
* @param {boolean} options.bypassCSP - Sets bypassing of page's Content-Security-Policy.
|
|
39
|
+
* @param {string} options.deviceName - Sets the device name of a current linked device., i.e.: 'TEST'.
|
|
40
|
+
* @param {string} options.browserName - Sets the browser name of a current linked device, i.e.: 'Firefox'.
|
|
39
41
|
* @param {object} options.proxyAuthentication - Proxy Authentication object.
|
|
40
42
|
*
|
|
41
43
|
* @fires Client#qr
|
|
@@ -94,7 +96,8 @@ class Client extends EventEmitter {
|
|
|
94
96
|
*/
|
|
95
97
|
async inject() {
|
|
96
98
|
await this.pupPage.waitForFunction('window.Debug?.VERSION != undefined', {timeout: this.options.authTimeoutMs});
|
|
97
|
-
|
|
99
|
+
await this.setDeviceName(this.options.deviceName, this.options.browserName);
|
|
100
|
+
const pairWithPhoneNumber = this.options.pairWithPhoneNumber;
|
|
98
101
|
const version = await this.getWWebVersion();
|
|
99
102
|
const isCometOrAbove = parseInt(version.split('.')?.[1]) >= 3000;
|
|
100
103
|
|
|
@@ -140,41 +143,55 @@ class Client extends EventEmitter {
|
|
|
140
143
|
return;
|
|
141
144
|
}
|
|
142
145
|
|
|
143
|
-
// Register qr events
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
146
|
+
// Register qr/code events
|
|
147
|
+
if (pairWithPhoneNumber.phoneNumber) {
|
|
148
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onCodeReceivedEvent', async (code) => {
|
|
149
|
+
/**
|
|
150
|
+
* Emitted when a pairing code is received
|
|
151
|
+
* @event Client#code
|
|
152
|
+
* @param {string} code Code
|
|
153
|
+
* @returns {string} Code that was just received
|
|
154
|
+
*/
|
|
155
|
+
this.emit(Events.CODE_RECEIVED, code);
|
|
156
|
+
return code;
|
|
157
|
+
});
|
|
158
|
+
this.requestPairingCode(pairWithPhoneNumber.phoneNumber, pairWithPhoneNumber.showNotification, pairWithPhoneNumber.intervalMs);
|
|
159
|
+
} else {
|
|
160
|
+
let qrRetries = 0;
|
|
161
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onQRChangedEvent', async (qr) => {
|
|
162
|
+
/**
|
|
163
|
+
* Emitted when a QR code is received
|
|
164
|
+
* @event Client#qr
|
|
165
|
+
* @param {string} qr QR Code
|
|
166
|
+
*/
|
|
167
|
+
this.emit(Events.QR_RECEIVED, qr);
|
|
168
|
+
if (this.options.qrMaxRetries > 0) {
|
|
169
|
+
qrRetries++;
|
|
170
|
+
if (qrRetries > this.options.qrMaxRetries) {
|
|
171
|
+
this.emit(Events.DISCONNECTED, 'Max qrcode retries reached');
|
|
172
|
+
await this.destroy();
|
|
173
|
+
}
|
|
157
174
|
}
|
|
158
|
-
}
|
|
159
|
-
});
|
|
175
|
+
});
|
|
160
176
|
|
|
161
177
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
178
|
+
await this.pupPage.evaluate(async () => {
|
|
179
|
+
const registrationInfo = await window.AuthStore.RegistrationUtils.waSignalStore.getRegistrationInfo();
|
|
180
|
+
const noiseKeyPair = await window.AuthStore.RegistrationUtils.waNoiseInfo.get();
|
|
181
|
+
const staticKeyB64 = window.AuthStore.Base64Tools.encodeB64(noiseKeyPair.staticKeyPair.pubKey);
|
|
182
|
+
const identityKeyB64 = window.AuthStore.Base64Tools.encodeB64(registrationInfo.identityKeyPair.pubKey);
|
|
183
|
+
const advSecretKey = await window.AuthStore.RegistrationUtils.getADVSecretKey();
|
|
184
|
+
const platform = window.AuthStore.RegistrationUtils.DEVICE_PLATFORM;
|
|
185
|
+
const getQR = (ref) => ref + ',' + staticKeyB64 + ',' + identityKeyB64 + ',' + advSecretKey + ',' + platform;
|
|
186
|
+
|
|
187
|
+
window.onQRChangedEvent(getQR(window.AuthStore.Conn.ref)); // initial qr
|
|
188
|
+
window.AuthStore.Conn.on('change:ref', (_, ref) => { window.onQRChangedEvent(getQR(ref)); }); // future QR changes
|
|
189
|
+
});
|
|
190
|
+
}
|
|
174
191
|
}
|
|
175
192
|
|
|
176
193
|
await exposeFunctionIfAbsent(this.pupPage, 'onAuthAppStateChangedEvent', async (state) => {
|
|
177
|
-
if (state == 'UNPAIRED_IDLE') {
|
|
194
|
+
if (state == 'UNPAIRED_IDLE' && !pairWithPhoneNumber.phoneNumber) {
|
|
178
195
|
// refresh qr code
|
|
179
196
|
window.Store.Cmd.refreshQR();
|
|
180
197
|
}
|
|
@@ -343,15 +360,32 @@ class Client extends EventEmitter {
|
|
|
343
360
|
/**
|
|
344
361
|
* Request authentication via pairing code instead of QR code
|
|
345
362
|
* @param {string} phoneNumber - Phone number in international, symbol-free format (e.g. 12025550108 for US, 551155501234 for Brazil)
|
|
346
|
-
* @param {boolean} showNotification - Show notification to pair on phone number
|
|
363
|
+
* @param {boolean} [showNotification = true] - Show notification to pair on phone number
|
|
364
|
+
* @param {number} [intervalMs = 180000] - The interval in milliseconds on how frequent to generate pairing code (WhatsApp default to 3 minutes)
|
|
347
365
|
* @returns {Promise<string>} - Returns a pairing code in format "ABCDEFGH"
|
|
348
366
|
*/
|
|
349
|
-
async requestPairingCode(phoneNumber, showNotification = true) {
|
|
350
|
-
return await this.pupPage.evaluate(async (phoneNumber, showNotification) => {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
367
|
+
async requestPairingCode(phoneNumber, showNotification = true, intervalMs = 180000) {
|
|
368
|
+
return await this.pupPage.evaluate(async (phoneNumber, showNotification, intervalMs) => {
|
|
369
|
+
const getCode = async () => {
|
|
370
|
+
while (!window.AuthStore.PairingCodeLinkUtils) {
|
|
371
|
+
await new Promise(resolve => setTimeout(resolve, 250));
|
|
372
|
+
}
|
|
373
|
+
window.AuthStore.PairingCodeLinkUtils.setPairingType('ALT_DEVICE_LINKING');
|
|
374
|
+
await window.AuthStore.PairingCodeLinkUtils.initializeAltDeviceLinking();
|
|
375
|
+
return window.AuthStore.PairingCodeLinkUtils.startAltLinkingFlow(phoneNumber, showNotification);
|
|
376
|
+
};
|
|
377
|
+
if (window.codeInterval) {
|
|
378
|
+
clearInterval(window.codeInterval); // remove existing interval
|
|
379
|
+
}
|
|
380
|
+
window.codeInterval = setInterval(async () => {
|
|
381
|
+
if (window.AuthStore.AppState.state != 'UNPAIRED' && window.AuthStore.AppState.state != 'UNPAIRED_IDLE') {
|
|
382
|
+
clearInterval(window.codeInterval);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
window.onCodeReceivedEvent(await getCode());
|
|
386
|
+
}, intervalMs);
|
|
387
|
+
return window.onCodeReceivedEvent(await getCode());
|
|
388
|
+
}, phoneNumber, showNotification, intervalMs);
|
|
355
389
|
}
|
|
356
390
|
|
|
357
391
|
/**
|
|
@@ -434,6 +468,9 @@ class Client extends EventEmitter {
|
|
|
434
468
|
let revoked_msg;
|
|
435
469
|
if (last_message && msg.id.id === last_message.id.id) {
|
|
436
470
|
revoked_msg = new Message(this, last_message);
|
|
471
|
+
|
|
472
|
+
if (message.protocolMessageKey)
|
|
473
|
+
revoked_msg.id = { ...message.protocolMessageKey };
|
|
437
474
|
}
|
|
438
475
|
|
|
439
476
|
/**
|
|
@@ -672,14 +709,15 @@ class Client extends EventEmitter {
|
|
|
672
709
|
this.emit(Events.MESSAGE_CIPHERTEXT, new Message(this, msg));
|
|
673
710
|
});
|
|
674
711
|
|
|
675
|
-
await exposeFunctionIfAbsent(this.pupPage, 'onPollVoteEvent', (
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
712
|
+
await exposeFunctionIfAbsent(this.pupPage, 'onPollVoteEvent', (votes) => {
|
|
713
|
+
for (const vote of votes) {
|
|
714
|
+
/**
|
|
715
|
+
* Emitted when some poll option is selected or deselected,
|
|
716
|
+
* shows a user's current selected option(s) on the poll
|
|
717
|
+
* @event Client#vote_update
|
|
718
|
+
*/
|
|
719
|
+
this.emit(Events.VOTE_UPDATE, new PollVote(this, vote));
|
|
720
|
+
}
|
|
683
721
|
});
|
|
684
722
|
|
|
685
723
|
await this.pupPage.evaluate(() => {
|
|
@@ -706,10 +744,6 @@ class Client extends EventEmitter {
|
|
|
706
744
|
}
|
|
707
745
|
});
|
|
708
746
|
window.Store.Chat.on('change:unreadCount', (chat) => {window.onChatUnreadCountEvent(chat);});
|
|
709
|
-
window.Store.PollVote.on('add', async (vote) => {
|
|
710
|
-
const pollVoteModel = await window.WWebJS.getPollVoteModel(vote);
|
|
711
|
-
pollVoteModel && window.onPollVoteEvent(pollVoteModel);
|
|
712
|
-
});
|
|
713
747
|
|
|
714
748
|
if (window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.1014111620')) {
|
|
715
749
|
const module = window.Store.AddonReactionTable;
|
|
@@ -727,6 +761,39 @@ class Client extends EventEmitter {
|
|
|
727
761
|
|
|
728
762
|
return ogMethod(...args);
|
|
729
763
|
}).bind(module);
|
|
764
|
+
|
|
765
|
+
const pollVoteModule = window.Store.AddonPollVoteTable;
|
|
766
|
+
const ogPollVoteMethod = pollVoteModule.bulkUpsert;
|
|
767
|
+
|
|
768
|
+
pollVoteModule.bulkUpsert = (async (...args) => {
|
|
769
|
+
const votes = await Promise.all(args[0].map(async vote => {
|
|
770
|
+
const msgKey = vote.id;
|
|
771
|
+
const parentMsgKey = vote.pollUpdateParentKey;
|
|
772
|
+
const timestamp = vote.t / 1000;
|
|
773
|
+
const sender = vote.author ?? vote.from;
|
|
774
|
+
const senderUserJid = sender._serialized;
|
|
775
|
+
|
|
776
|
+
let parentMessage = window.Store.Msg.get(parentMsgKey._serialized);
|
|
777
|
+
if (!parentMessage) {
|
|
778
|
+
const fetched = await window.Store.Msg.getMessagesById([parentMsgKey._serialized]);
|
|
779
|
+
parentMessage = fetched?.messages?.[0] || null;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return {
|
|
783
|
+
...vote,
|
|
784
|
+
msgKey,
|
|
785
|
+
sender,
|
|
786
|
+
parentMsgKey,
|
|
787
|
+
senderUserJid,
|
|
788
|
+
timestamp,
|
|
789
|
+
parentMessage
|
|
790
|
+
};
|
|
791
|
+
}));
|
|
792
|
+
|
|
793
|
+
window.onPollVoteEvent(votes);
|
|
794
|
+
|
|
795
|
+
return ogPollVoteMethod.apply(pollVoteModule, args);
|
|
796
|
+
}).bind(pollVoteModule);
|
|
730
797
|
} else {
|
|
731
798
|
const module = window.Store.createOrUpdateReactionsModule;
|
|
732
799
|
const ogMethod = module.createOrUpdateReactions;
|
|
@@ -813,6 +880,19 @@ class Client extends EventEmitter {
|
|
|
813
880
|
});
|
|
814
881
|
}
|
|
815
882
|
|
|
883
|
+
async setDeviceName(deviceName, browserName) {
|
|
884
|
+
(deviceName || browserName) && await this.pupPage.evaluate((deviceName, browserName) => {
|
|
885
|
+
const func = window.require('WAWebMiscBrowserUtils').info;
|
|
886
|
+
window.require('WAWebMiscBrowserUtils').info = () => {
|
|
887
|
+
return {
|
|
888
|
+
...func(),
|
|
889
|
+
...(deviceName ? { os: deviceName } : {}),
|
|
890
|
+
...(browserName ? { name: browserName } : {})
|
|
891
|
+
};
|
|
892
|
+
};
|
|
893
|
+
}, deviceName, browserName);
|
|
894
|
+
}
|
|
895
|
+
|
|
816
896
|
/**
|
|
817
897
|
* Mark as seen for the Chat
|
|
818
898
|
* @param {string} chatId
|
|
@@ -853,6 +933,7 @@ class Client extends EventEmitter {
|
|
|
853
933
|
* @property {string} [stickerName=undefined] - Sets the name of the sticker, (if sendMediaAsSticker is true).
|
|
854
934
|
* @property {string[]} [stickerCategories=undefined] - Sets the categories of the sticker, (if sendMediaAsSticker is true). Provide emoji char array, can be null.
|
|
855
935
|
* @property {boolean} [ignoreQuoteErrors = true] - Should the bot send a quoted message without the quoted message if it fails to get the quote?
|
|
936
|
+
* @property {boolean} [waitUntilMsgSent = false] - Should the bot wait for the message send result?
|
|
856
937
|
* @property {MessageMedia} [media] - Media to be sent
|
|
857
938
|
* @property {any} [extra] - Extra options
|
|
858
939
|
*/
|
|
@@ -903,6 +984,7 @@ class Client extends EventEmitter {
|
|
|
903
984
|
groupMentions: options.groupMentions,
|
|
904
985
|
invokedBotWid: options.invokedBotWid,
|
|
905
986
|
ignoreQuoteErrors: options.ignoreQuoteErrors !== false,
|
|
987
|
+
waitUntilMsgSent: options.waitUntilMsgSent || false,
|
|
906
988
|
extraOptions: options.extra
|
|
907
989
|
};
|
|
908
990
|
|
|
@@ -923,6 +1005,9 @@ class Client extends EventEmitter {
|
|
|
923
1005
|
} else if (content instanceof Poll) {
|
|
924
1006
|
internalOptions.poll = content;
|
|
925
1007
|
content = '';
|
|
1008
|
+
} else if (content instanceof ScheduledEvent) {
|
|
1009
|
+
internalOptions.event = content;
|
|
1010
|
+
content = '';
|
|
926
1011
|
} else if (content instanceof Contact) {
|
|
927
1012
|
internalOptions.contactCard = content.id._serialized;
|
|
928
1013
|
content = '';
|
|
@@ -1127,6 +1212,29 @@ class Client extends EventEmitter {
|
|
|
1127
1212
|
return null;
|
|
1128
1213
|
}
|
|
1129
1214
|
|
|
1215
|
+
/**
|
|
1216
|
+
* Gets instances of all pinned messages in a chat
|
|
1217
|
+
* @param {string} chatId The chat ID
|
|
1218
|
+
* @returns {Promise<[Message]|[]>}
|
|
1219
|
+
*/
|
|
1220
|
+
async getPinnedMessages(chatId) {
|
|
1221
|
+
const pinnedMsgs = await this.pupPage.evaluate(async (chatId) => {
|
|
1222
|
+
const chatWid = window.Store.WidFactory.createWid(chatId);
|
|
1223
|
+
const chat = window.Store.Chat.get(chatWid) ?? await window.Store.Chat.find(chatWid);
|
|
1224
|
+
if (!chat) return [];
|
|
1225
|
+
|
|
1226
|
+
const msgs = await window.Store.PinnedMsgUtils.getTable().equals(['chatId'], chatWid.toString());
|
|
1227
|
+
|
|
1228
|
+
const pinnedMsgs = msgs.map((msg) => window.Store.Msg.get(msg.parentMsgKey));
|
|
1229
|
+
|
|
1230
|
+
return !pinnedMsgs.length
|
|
1231
|
+
? []
|
|
1232
|
+
: pinnedMsgs.map((msg) => window.WWebJS.getMessageModel(msg));
|
|
1233
|
+
}, chatId);
|
|
1234
|
+
|
|
1235
|
+
return pinnedMsgs.map((msg) => new Message(this, msg));
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1130
1238
|
/**
|
|
1131
1239
|
* Returns an object with information about the invite code's group
|
|
1132
1240
|
* @param {string} inviteCode
|
|
@@ -1528,6 +1636,10 @@ class Client extends EventEmitter {
|
|
|
1528
1636
|
* @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)
|
|
1529
1637
|
* @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)
|
|
1530
1638
|
* @property {string} [comment = ''] The comment to be added to an inviteV4 (empty string by default)
|
|
1639
|
+
* @property {boolean} [memberAddMode = false] If true, only admins can add members to the group (false by default)
|
|
1640
|
+
* @property {boolean} [membershipApprovalMode = false] If true, group admins will be required to approve anyone who wishes to join the group (false by default)
|
|
1641
|
+
* @property {boolean} [isRestrict = true] If true, only admins can change group group info (true by default)
|
|
1642
|
+
* @property {boolean} [isAnnounce = false] If true, only admins can send messages (false by default)
|
|
1531
1643
|
*/
|
|
1532
1644
|
|
|
1533
1645
|
/**
|
|
@@ -1542,7 +1654,12 @@ class Client extends EventEmitter {
|
|
|
1542
1654
|
participants.map(p => (p instanceof Contact) ? p.id._serialized : p);
|
|
1543
1655
|
|
|
1544
1656
|
return await this.pupPage.evaluate(async (title, participants, options) => {
|
|
1545
|
-
const {
|
|
1657
|
+
const {
|
|
1658
|
+
messageTimer = 0,
|
|
1659
|
+
parentGroupId,
|
|
1660
|
+
autoSendInviteV4 = true,
|
|
1661
|
+
comment = '',
|
|
1662
|
+
} = options;
|
|
1546
1663
|
const participantData = {}, participantWids = [], failedParticipants = [];
|
|
1547
1664
|
let createGroupResult, parentGroupWid;
|
|
1548
1665
|
|
|
@@ -1555,7 +1672,9 @@ class Client extends EventEmitter {
|
|
|
1555
1672
|
|
|
1556
1673
|
for (const participant of participants) {
|
|
1557
1674
|
const pWid = window.Store.WidFactory.createWid(participant);
|
|
1558
|
-
if ((await window.Store.QueryExist(pWid))?.wid)
|
|
1675
|
+
if ((await window.Store.QueryExist(pWid))?.wid) {
|
|
1676
|
+
participantWids.push({ phoneNumber: pWid });
|
|
1677
|
+
}
|
|
1559
1678
|
else failedParticipants.push(participant);
|
|
1560
1679
|
}
|
|
1561
1680
|
|
|
@@ -1564,14 +1683,13 @@ class Client extends EventEmitter {
|
|
|
1564
1683
|
try {
|
|
1565
1684
|
createGroupResult = await window.Store.GroupUtils.createGroup(
|
|
1566
1685
|
{
|
|
1567
|
-
'
|
|
1568
|
-
'
|
|
1569
|
-
'
|
|
1686
|
+
'addressingModeOverride': 'lid',
|
|
1687
|
+
'memberAddMode': options.memberAddMode ?? false,
|
|
1688
|
+
'membershipApprovalMode': options.membershipApprovalMode ?? false,
|
|
1689
|
+
'announce': options.announce ?? false,
|
|
1690
|
+
'restrict': options.isRestrict !== undefined ? !options.isRestrict : false,
|
|
1570
1691
|
'ephemeralDuration': messageTimer,
|
|
1571
|
-
'full': undefined,
|
|
1572
1692
|
'parentGroupId': parentGroupWid,
|
|
1573
|
-
'restrict': options.restrict === undefined ? true : options.restrict,
|
|
1574
|
-
'thumb': undefined,
|
|
1575
1693
|
'title': title,
|
|
1576
1694
|
},
|
|
1577
1695
|
participantWids
|
|
@@ -1582,13 +1700,14 @@ class Client extends EventEmitter {
|
|
|
1582
1700
|
|
|
1583
1701
|
for (const participant of createGroupResult.participants) {
|
|
1584
1702
|
let isInviteV4Sent = false;
|
|
1703
|
+
participant.wid.server == 'lid' && (participant.wid = window.Store.LidUtils.getPhoneNumber(participant.wid));
|
|
1585
1704
|
const participantId = participant.wid._serialized;
|
|
1586
1705
|
const statusCode = participant.error || 200;
|
|
1587
1706
|
|
|
1588
1707
|
if (autoSendInviteV4 && statusCode === 403) {
|
|
1589
1708
|
window.Store.Contact.gadd(participant.wid, { silent: true });
|
|
1590
1709
|
const addParticipantResult = await window.Store.GroupInviteV4.sendGroupInviteMessage(
|
|
1591
|
-
await window.Store.Chat.find(participant.wid),
|
|
1710
|
+
window.Store.Chat.get(participant.wid) || await window.Store.Chat.find(participant.wid),
|
|
1592
1711
|
createGroupResult.wid._serialized,
|
|
1593
1712
|
createGroupResult.subject,
|
|
1594
1713
|
participant.invite_code,
|
|
@@ -1596,9 +1715,7 @@ class Client extends EventEmitter {
|
|
|
1596
1715
|
comment,
|
|
1597
1716
|
await window.WWebJS.getProfilePicThumbToBase64(createGroupResult.wid)
|
|
1598
1717
|
);
|
|
1599
|
-
isInviteV4Sent =
|
|
1600
|
-
? addParticipantResult === 'OK'
|
|
1601
|
-
: addParticipantResult.messageSendResult === 'OK';
|
|
1718
|
+
isInviteV4Sent = addParticipantResult.messageSendResult === 'OK';
|
|
1602
1719
|
}
|
|
1603
1720
|
|
|
1604
1721
|
participantData[participantId] = {
|
|
@@ -2148,6 +2265,45 @@ class Client extends EventEmitter {
|
|
|
2148
2265
|
}, chatId);
|
|
2149
2266
|
}
|
|
2150
2267
|
|
|
2268
|
+
/**
|
|
2269
|
+
* Generates a WhatsApp call link (video call or voice call)
|
|
2270
|
+
* @param {Date} startTime The start time of the call
|
|
2271
|
+
* @param {string} callType The type of a WhatsApp call link to generate, valid values are: `video` | `voice`
|
|
2272
|
+
* @returns {Promise<string>} The WhatsApp call link (https://call.whatsapp.com/video/XxXxXxXxXxXxXx) or an empty string if a generation failed.
|
|
2273
|
+
*/
|
|
2274
|
+
async createCallLink(startTime, callType) {
|
|
2275
|
+
if (!['video', 'voice'].includes(callType)) {
|
|
2276
|
+
throw new class CreateCallLinkError extends Error {
|
|
2277
|
+
constructor(m) { super(m); }
|
|
2278
|
+
}('Invalid \'callType\' parameter value is provided. Valid values are: \'voice\' | \'video\'.');
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
startTime = Math.floor(startTime.getTime() / 1000);
|
|
2282
|
+
|
|
2283
|
+
return await this.pupPage.evaluate(async (startTimeTs, callType) => {
|
|
2284
|
+
const response = await window.Store.ScheduledEventMsgUtils.createEventCallLink(startTimeTs, callType);
|
|
2285
|
+
return response ?? '';
|
|
2286
|
+
}, startTime, callType);
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
/**
|
|
2290
|
+
* Sends a response to the scheduled event message, indicating whether a user is going to attend the event or not
|
|
2291
|
+
* @param {number} response The response code to the scheduled event message. Valid values are: `0` for NONE response (removes a previous response) | `1` for GOING | `2` for NOT GOING | `3` for MAYBE going
|
|
2292
|
+
* @param {string} eventMessageId The scheduled event message ID
|
|
2293
|
+
* @returns {Promise<boolean>}
|
|
2294
|
+
*/
|
|
2295
|
+
async sendResponseToScheduledEvent(response, eventMessageId) {
|
|
2296
|
+
if (![0, 1, 2, 3].includes(response)) return false;
|
|
2297
|
+
|
|
2298
|
+
return await this.pupPage.evaluate(async (response, msgId) => {
|
|
2299
|
+
const eventMsg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
|
|
2300
|
+
if (!eventMsg) return false;
|
|
2301
|
+
|
|
2302
|
+
await window.Store.ScheduledEventMsgUtils.sendEventResponseMsg(response, eventMsg);
|
|
2303
|
+
return true;
|
|
2304
|
+
}, response, eventMessageId);
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2151
2307
|
/**
|
|
2152
2308
|
* Save new contact to user's addressbook or edit the existing one
|
|
2153
2309
|
* @param {string} phoneNumber The contact's phone number in a format "17182222222", where "1" is a country code
|
|
@@ -2180,6 +2336,28 @@ class Client extends EventEmitter {
|
|
|
2180
2336
|
return await window.Store.AddressbookContactUtils.deleteContactAction(phoneNumber);
|
|
2181
2337
|
}, phoneNumber);
|
|
2182
2338
|
}
|
|
2339
|
+
|
|
2340
|
+
/**
|
|
2341
|
+
* Get lid and phone number for multiple users
|
|
2342
|
+
* @param {string[]} userIds - Array of user IDs
|
|
2343
|
+
* @returns {Promise<Array<{ lid: string, pn: string }>>}
|
|
2344
|
+
*/
|
|
2345
|
+
async getContactLidAndPhone(userIds) {
|
|
2346
|
+
return await this.pupPage.evaluate((userIds) => {
|
|
2347
|
+
!Array.isArray(userIds) && (userIds = [userIds]);
|
|
2348
|
+
return userIds.map(userId => {
|
|
2349
|
+
const wid = window.Store.WidFactory.createWid(userId);
|
|
2350
|
+
const isLid = wid.server === 'lid';
|
|
2351
|
+
const lid = isLid ? wid : window.Store.LidUtils.getCurrentLid(wid);
|
|
2352
|
+
const phone = isLid ? window.Store.LidUtils.getPhoneNumber(wid) : wid;
|
|
2353
|
+
|
|
2354
|
+
return {
|
|
2355
|
+
lid: lid._serialized,
|
|
2356
|
+
pn: phone._serialized
|
|
2357
|
+
};
|
|
2358
|
+
});
|
|
2359
|
+
}, userIds);
|
|
2360
|
+
}
|
|
2183
2361
|
}
|
|
2184
2362
|
|
|
2185
2363
|
module.exports = Client;
|
package/src/structures/Chat.js
CHANGED
|
@@ -279,6 +279,14 @@ class Chat extends Base {
|
|
|
279
279
|
return this.client.addOrRemoveLabels(labelIds, [this.id._serialized]);
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Gets instances of all pinned messages in a chat
|
|
284
|
+
* @returns {Promise<[Message]|[]>}
|
|
285
|
+
*/
|
|
286
|
+
async getPinnedMessages() {
|
|
287
|
+
return this.client.getPinnedMessages(this.id._serialized);
|
|
288
|
+
}
|
|
289
|
+
|
|
282
290
|
/**
|
|
283
291
|
* Sync chat history conversation
|
|
284
292
|
* @return {Promise<boolean>} True if operation completed successfully, false otherwise.
|
|
@@ -82,7 +82,7 @@ class GroupChat extends Chat {
|
|
|
82
82
|
|
|
83
83
|
!Array.isArray(participantIds) && (participantIds = [participantIds]);
|
|
84
84
|
const groupWid = window.Store.WidFactory.createWid(groupId);
|
|
85
|
-
const group = await window.Store.Chat.find(groupWid);
|
|
85
|
+
const group = window.Store.Chat.get(groupWid) || (await window.Store.Chat.find(groupWid));
|
|
86
86
|
const participantWids = participantIds.map((p) => window.Store.WidFactory.createWid(p));
|
|
87
87
|
|
|
88
88
|
const errorCodes = {
|
|
@@ -99,8 +99,8 @@ class GroupChat extends Chat {
|
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
await window.Store.GroupQueryAndUpdate({ id: groupId });
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
|
|
103
|
+
let groupParticipants = group.groupMetadata?.participants.serialize();
|
|
104
104
|
|
|
105
105
|
if (!groupParticipants) {
|
|
106
106
|
return errorCodes.isGroupEmpty;
|
|
@@ -110,6 +110,10 @@ class GroupChat extends Chat {
|
|
|
110
110
|
return errorCodes.iAmNotAdmin;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
groupParticipants.map(({ id }) => {
|
|
114
|
+
return id.server === 'lid' ? window.Store.LidUtils.getPhoneNumber(id) : id;
|
|
115
|
+
});
|
|
116
|
+
|
|
113
117
|
const _getSleepTime = (sleep) => {
|
|
114
118
|
if (!Array.isArray(sleep) || sleep.length === 2 && sleep[0] === sleep[1]) {
|
|
115
119
|
return sleep;
|
|
@@ -121,16 +125,17 @@ class GroupChat extends Chat {
|
|
|
121
125
|
return Math.floor(Math.random() * (sleep[1] - sleep[0] + 1)) + sleep[0];
|
|
122
126
|
};
|
|
123
127
|
|
|
124
|
-
for (
|
|
128
|
+
for (let pWid of participantWids) {
|
|
125
129
|
const pId = pWid._serialized;
|
|
126
|
-
|
|
130
|
+
pWid = pWid.server === 'lid' ? window.Store.LidUtils.getPhoneNumber(pWid) : pWid;
|
|
131
|
+
|
|
127
132
|
participantData[pId] = {
|
|
128
133
|
code: undefined,
|
|
129
134
|
message: undefined,
|
|
130
135
|
isInviteV4Sent: false
|
|
131
136
|
};
|
|
132
137
|
|
|
133
|
-
if (groupParticipants.some(p => p.
|
|
138
|
+
if (groupParticipants.some(p => p._serialized === pId)) {
|
|
134
139
|
participantData[pId].code = 409;
|
|
135
140
|
participantData[pId].message = errorCodes[409];
|
|
136
141
|
continue;
|
|
@@ -143,7 +148,7 @@ class GroupChat extends Chat {
|
|
|
143
148
|
}
|
|
144
149
|
|
|
145
150
|
const rpcResult =
|
|
146
|
-
await window.WWebJS.getAddParticipantsRpcResult(
|
|
151
|
+
await window.WWebJS.getAddParticipantsRpcResult(groupWid, pWid);
|
|
147
152
|
const { code: rpcResultCode } = rpcResult;
|
|
148
153
|
|
|
149
154
|
participantData[pId].code = rpcResultCode;
|
|
@@ -155,7 +160,7 @@ class GroupChat extends Chat {
|
|
|
155
160
|
window.Store.Contact.gadd(pWid, { silent: true });
|
|
156
161
|
|
|
157
162
|
if (rpcResult.name === 'ParticipantRequestCodeCanBeSent' &&
|
|
158
|
-
(userChat = await window.Store.Chat.find(pWid))) {
|
|
163
|
+
(userChat = window.Store.Chat.get(pWid) || (await window.Store.Chat.find(pWid)))) {
|
|
159
164
|
const groupName = group.formattedTitle || group.name;
|
|
160
165
|
const res = await window.Store.GroupInviteV4.sendGroupInviteMessage(
|
|
161
166
|
userChat,
|
|
@@ -166,9 +171,7 @@ class GroupChat extends Chat {
|
|
|
166
171
|
comment,
|
|
167
172
|
await window.WWebJS.getProfilePicThumbToBase64(groupWid)
|
|
168
173
|
);
|
|
169
|
-
isInviteV4Sent =
|
|
170
|
-
? res === 'OK'
|
|
171
|
-
: res.messageSendResult === 'OK';
|
|
174
|
+
isInviteV4Sent = res.messageSendResult === 'OK';
|
|
172
175
|
}
|
|
173
176
|
|
|
174
177
|
participantData[pId].isInviteV4Sent = isInviteV4Sent;
|
|
@@ -193,7 +196,10 @@ class GroupChat extends Chat {
|
|
|
193
196
|
return await this.client.pupPage.evaluate(async (chatId, participantIds) => {
|
|
194
197
|
const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
|
|
195
198
|
const participants = participantIds.map(p => {
|
|
196
|
-
|
|
199
|
+
const wid = window.Store.WidFactory.createWid(p);
|
|
200
|
+
const lid = wid.server!=='lid' ? window.Store.LidUtils.getCurrentLid(wid) : wid;
|
|
201
|
+
const phone = wid.server=='lid' ? window.Store.LidUtils.getPhoneNumber(wid) : wid;
|
|
202
|
+
return chat.groupMetadata.participants.get(lid?._serialized) || chat.groupMetadata.participants.get(phone?._serialized);
|
|
197
203
|
}).filter(p => Boolean(p));
|
|
198
204
|
await window.Store.GroupParticipants.removeParticipants(chat, participants);
|
|
199
205
|
return { status: 200 };
|
|
@@ -209,7 +215,10 @@ class GroupChat extends Chat {
|
|
|
209
215
|
return await this.client.pupPage.evaluate(async (chatId, participantIds) => {
|
|
210
216
|
const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
|
|
211
217
|
const participants = participantIds.map(p => {
|
|
212
|
-
|
|
218
|
+
const wid = window.Store.WidFactory.createWid(p);
|
|
219
|
+
const lid = wid.server!=='lid' ? window.Store.LidUtils.getCurrentLid(wid) : wid;
|
|
220
|
+
const phone = wid.server=='lid' ? window.Store.LidUtils.getPhoneNumber(wid) : wid;
|
|
221
|
+
return chat.groupMetadata.participants.get(lid?._serialized) || chat.groupMetadata.participants.get(phone?._serialized);
|
|
213
222
|
}).filter(p => Boolean(p));
|
|
214
223
|
await window.Store.GroupParticipants.promoteParticipants(chat, participants);
|
|
215
224
|
return { status: 200 };
|
|
@@ -225,7 +234,10 @@ class GroupChat extends Chat {
|
|
|
225
234
|
return await this.client.pupPage.evaluate(async (chatId, participantIds) => {
|
|
226
235
|
const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
|
|
227
236
|
const participants = participantIds.map(p => {
|
|
228
|
-
|
|
237
|
+
const wid = window.Store.WidFactory.createWid(p);
|
|
238
|
+
const lid = wid.server!=='lid' ? window.Store.LidUtils.getCurrentLid(wid) : wid;
|
|
239
|
+
const phone = wid.server=='lid' ? window.Store.LidUtils.getPhoneNumber(wid) : wid;
|
|
240
|
+
return chat.groupMetadata.participants.get(lid?._serialized) || chat.groupMetadata.participants.get(phone?._serialized);
|
|
229
241
|
}).filter(p => Boolean(p));
|
|
230
242
|
await window.Store.GroupParticipants.demoteParticipants(chat, participants);
|
|
231
243
|
return { status: 200 };
|