Sends a response to the scheduled event message, indicating whether a user is going to attend the event or not
+
+
+
+
Parameters
+
+
+
+
Name
+
Type
+
Optional
+
Description
+
+
+
+
+
+
response
+
+
+
number
+
+
+
+
+
+
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
+
+
+
+
+
+
eventMessageId
+
+
+
string
+
+
+
+
+
+
The scheduled event message ID
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns
+
Promise containing boolean
@@ -5720,6 +6071,71 @@ Note: the user you are transferring the channel ownership to must be a channel a
+
+
+
+
+
+
code
Emitted when a pairing code is received
+
+
+
+
Parameter
+
+
+
+
Name
+
Type
+
Optional
+
Description
+
+
+
+
+
+
code
+
+
+
string
+
+
+
+
+
+
Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Returns
+
string
Code that was just received
@@ -7383,7 +7799,7 @@ shows a user's current selected option(s) on the poll
diff --git a/docs/Client.js.html b/docs/Client.js.html
index 3b7cf43..5c82890 100644
--- a/docs/Client.js.html
+++ b/docs/Client.js.html
@@ -4,7 +4,7 @@
- whatsapp-web.js 1.31.1-alpha.0 » Source: Client.js
+ whatsapp-web.js 1.32.0 » Source: Client.js
@@ -15,7 +15,7 @@
@@ -46,7 +46,7 @@ const { LoadUtils } = require('./util/Injected/Utils');
const ChatFactory = require('./factories/ChatFactory');
const ContactFactory = require('./factories/ContactFactory');
const WebCacheFactory = require('./webCache/WebCacheFactory');
-const { Broadcast, Buttons, Call, ClientInfo, Contact, GroupNotification, Label, List, Location, Message, MessageMedia, Poll, PollVote, Reaction } = require('./structures');
+const { ClientInfo, Message, MessageMedia, Contact, Location, Poll, PollVote, GroupNotification, Label, Call, Buttons, List, Reaction, Broadcast, ScheduledEvent } = require('./structures');
const NoAuth = require('./authStrategies/NoAuth');
const {exposeFunctionIfAbsent} = require('./util/Puppeteer');
@@ -67,6 +67,8 @@ const {exposeFunctionIfAbsent} = require('./util/Puppeteer');
* @param {string} options.userAgent - User agent to use in puppeteer
* @param {string} options.ffmpegPath - Ffmpeg path to use when formatting videos to webp while sending stickers
* @param {boolean} options.bypassCSP - Sets bypassing of page's Content-Security-Policy.
+ * @param {string} options.deviceName - Sets the device name of a current linked device., i.e.: 'TEST'.
+ * @param {string} options.browserName - Sets the browser name of a current linked device, i.e.: 'Firefox'.
* @param {object} options.proxyAuthentication - Proxy Authentication object.
*
* @fires Client#qr
@@ -125,7 +127,8 @@ class Client extends EventEmitter {
*/
async inject() {
await this.pupPage.waitForFunction('window.Debug?.VERSION != undefined', {timeout: this.options.authTimeoutMs});
-
+ await this.setDeviceName(this.options.deviceName, this.options.browserName);
+ const pairWithPhoneNumber = this.options.pairWithPhoneNumber;
const version = await this.getWWebVersion();
const isCometOrAbove = parseInt(version.split('.')?.[1]) >= 3000;
@@ -171,41 +174,55 @@ class Client extends EventEmitter {
return;
}
- // Register qr events
- let qrRetries = 0;
- await exposeFunctionIfAbsent(this.pupPage, 'onQRChangedEvent', async (qr) => {
- /**
- * Emitted when a QR code is received
- * @event Client#qr
- * @param {string} qr QR Code
- */
- this.emit(Events.QR_RECEIVED, qr);
- if (this.options.qrMaxRetries > 0) {
- qrRetries++;
- if (qrRetries > this.options.qrMaxRetries) {
- this.emit(Events.DISCONNECTED, 'Max qrcode retries reached');
- await this.destroy();
+ // Register qr/code events
+ if (pairWithPhoneNumber.phoneNumber) {
+ await exposeFunctionIfAbsent(this.pupPage, 'onCodeReceivedEvent', async (code) => {
+ /**
+ * Emitted when a pairing code is received
+ * @event Client#code
+ * @param {string} code Code
+ * @returns {string} Code that was just received
+ */
+ this.emit(Events.CODE_RECEIVED, code);
+ return code;
+ });
+ this.requestPairingCode(pairWithPhoneNumber.phoneNumber, pairWithPhoneNumber.showNotification, pairWithPhoneNumber.intervalMs);
+ } else {
+ let qrRetries = 0;
+ await exposeFunctionIfAbsent(this.pupPage, 'onQRChangedEvent', async (qr) => {
+ /**
+ * Emitted when a QR code is received
+ * @event Client#qr
+ * @param {string} qr QR Code
+ */
+ this.emit(Events.QR_RECEIVED, qr);
+ if (this.options.qrMaxRetries > 0) {
+ qrRetries++;
+ if (qrRetries > this.options.qrMaxRetries) {
+ this.emit(Events.DISCONNECTED, 'Max qrcode retries reached');
+ await this.destroy();
+ }
}
- }
- });
+ });
- await this.pupPage.evaluate(async () => {
- const registrationInfo = await window.AuthStore.RegistrationUtils.waSignalStore.getRegistrationInfo();
- const noiseKeyPair = await window.AuthStore.RegistrationUtils.waNoiseInfo.get();
- const staticKeyB64 = window.AuthStore.Base64Tools.encodeB64(noiseKeyPair.staticKeyPair.pubKey);
- const identityKeyB64 = window.AuthStore.Base64Tools.encodeB64(registrationInfo.identityKeyPair.pubKey);
- const advSecretKey = await window.AuthStore.RegistrationUtils.getADVSecretKey();
- const platform = window.AuthStore.RegistrationUtils.DEVICE_PLATFORM;
- const getQR = (ref) => ref + ',' + staticKeyB64 + ',' + identityKeyB64 + ',' + advSecretKey + ',' + platform;
-
- window.onQRChangedEvent(getQR(window.AuthStore.Conn.ref)); // initial qr
- window.AuthStore.Conn.on('change:ref', (_, ref) => { window.onQRChangedEvent(getQR(ref)); }); // future QR changes
- });
+ await this.pupPage.evaluate(async () => {
+ const registrationInfo = await window.AuthStore.RegistrationUtils.waSignalStore.getRegistrationInfo();
+ const noiseKeyPair = await window.AuthStore.RegistrationUtils.waNoiseInfo.get();
+ const staticKeyB64 = window.AuthStore.Base64Tools.encodeB64(noiseKeyPair.staticKeyPair.pubKey);
+ const identityKeyB64 = window.AuthStore.Base64Tools.encodeB64(registrationInfo.identityKeyPair.pubKey);
+ const advSecretKey = await window.AuthStore.RegistrationUtils.getADVSecretKey();
+ const platform = window.AuthStore.RegistrationUtils.DEVICE_PLATFORM;
+ const getQR = (ref) => ref + ',' + staticKeyB64 + ',' + identityKeyB64 + ',' + advSecretKey + ',' + platform;
+
+ window.onQRChangedEvent(getQR(window.AuthStore.Conn.ref)); // initial qr
+ window.AuthStore.Conn.on('change:ref', (_, ref) => { window.onQRChangedEvent(getQR(ref)); }); // future QR changes
+ });
+ }
}
await exposeFunctionIfAbsent(this.pupPage, 'onAuthAppStateChangedEvent', async (state) => {
- if (state == 'UNPAIRED_IDLE') {
+ if (state == 'UNPAIRED_IDLE' && !pairWithPhoneNumber.phoneNumber) {
// refresh qr code
window.Store.Cmd.refreshQR();
}
@@ -374,15 +391,32 @@ class Client extends EventEmitter {
/**
* Request authentication via pairing code instead of QR code
* @param {string} phoneNumber - Phone number in international, symbol-free format (e.g. 12025550108 for US, 551155501234 for Brazil)
- * @param {boolean} showNotification - Show notification to pair on phone number
+ * @param {boolean} [showNotification = true] - Show notification to pair on phone number
+ * @param {number} [intervalMs = 180000] - The interval in milliseconds on how frequent to generate pairing code (WhatsApp default to 3 minutes)
* @returns {Promise<string>} - Returns a pairing code in format "ABCDEFGH"
*/
- async requestPairingCode(phoneNumber, showNotification = true) {
- return await this.pupPage.evaluate(async (phoneNumber, showNotification) => {
- window.AuthStore.PairingCodeLinkUtils.setPairingType('ALT_DEVICE_LINKING');
- await window.AuthStore.PairingCodeLinkUtils.initializeAltDeviceLinking();
- return window.AuthStore.PairingCodeLinkUtils.startAltLinkingFlow(phoneNumber, showNotification);
- }, phoneNumber, showNotification);
+ async requestPairingCode(phoneNumber, showNotification = true, intervalMs = 180000) {
+ return await this.pupPage.evaluate(async (phoneNumber, showNotification, intervalMs) => {
+ const getCode = async () => {
+ while (!window.AuthStore.PairingCodeLinkUtils) {
+ await new Promise(resolve => setTimeout(resolve, 250));
+ }
+ window.AuthStore.PairingCodeLinkUtils.setPairingType('ALT_DEVICE_LINKING');
+ await window.AuthStore.PairingCodeLinkUtils.initializeAltDeviceLinking();
+ return window.AuthStore.PairingCodeLinkUtils.startAltLinkingFlow(phoneNumber, showNotification);
+ };
+ if (window.codeInterval) {
+ clearInterval(window.codeInterval); // remove existing interval
+ }
+ window.codeInterval = setInterval(async () => {
+ if (window.AuthStore.AppState.state != 'UNPAIRED' && window.AuthStore.AppState.state != 'UNPAIRED_IDLE') {
+ clearInterval(window.codeInterval);
+ return;
+ }
+ window.onCodeReceivedEvent(await getCode());
+ }, intervalMs);
+ return window.onCodeReceivedEvent(await getCode());
+ }, phoneNumber, showNotification, intervalMs);
}
/**
@@ -844,6 +878,19 @@ class Client extends EventEmitter {
});
}
+ async setDeviceName(deviceName, browserName) {
+ (deviceName || browserName) && await this.pupPage.evaluate((deviceName, browserName) => {
+ const func = window.require('WAWebMiscBrowserUtils').info;
+ window.require('WAWebMiscBrowserUtils').info = () => {
+ return {
+ ...func(),
+ ...(deviceName ? { os: deviceName } : {}),
+ ...(browserName ? { name: browserName } : {})
+ };
+ };
+ }, deviceName, browserName);
+ }
+
/**
* Mark as seen for the Chat
* @param {string} chatId
@@ -954,6 +1001,9 @@ class Client extends EventEmitter {
} else if (content instanceof Poll) {
internalOptions.poll = content;
content = '';
+ } else if (content instanceof ScheduledEvent) {
+ internalOptions.event = content;
+ content = '';
} else if (content instanceof Contact) {
internalOptions.contactCard = content.id._serialized;
content = '';
@@ -1158,6 +1208,29 @@ class Client extends EventEmitter {
return null;
}
+ /**
+ * Gets instances of all pinned messages in a chat
+ * @param {string} chatId The chat ID
+ * @returns {Promise<[Message]|[]>}
+ */
+ async getPinnedMessages(chatId) {
+ const pinnedMsgs = await this.pupPage.evaluate(async (chatId) => {
+ const chatWid = window.Store.WidFactory.createWid(chatId);
+ const chat = window.Store.Chat.get(chatWid) ?? await window.Store.Chat.find(chatWid);
+ if (!chat) return [];
+
+ const msgs = await window.Store.PinnedMsgUtils.getTable().equals(['chatId'], chatWid.toString());
+
+ const pinnedMsgs = msgs.map((msg) => window.Store.Msg.get(msg.parentMsgKey));
+
+ return !pinnedMsgs.length
+ ? []
+ : pinnedMsgs.map((msg) => window.WWebJS.getMessageModel(msg));
+ }, chatId);
+
+ return pinnedMsgs.map((msg) => new Message(this, msg));
+ }
+
/**
* Returns an object with information about the invite code's group
* @param {string} inviteCode
@@ -1559,6 +1632,10 @@ class Client extends EventEmitter {
* @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)
* @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)
* @property {string} [comment = ''] The comment to be added to an inviteV4 (empty string by default)
+ * @property {boolean} [memberAddMode = false] If true, only admins can add members to the group (false by default)
+ * @property {boolean} [membershipApprovalMode = false] If true, group admins will be required to approve anyone who wishes to join the group (false by default)
+ * @property {boolean} [isRestrict = true] If true, only admins can change group group info (true by default)
+ * @property {boolean} [isAnnounce = false] If true, only admins can send messages (false by default)
*/
/**
@@ -1573,7 +1650,12 @@ class Client extends EventEmitter {
participants.map(p => (p instanceof Contact) ? p.id._serialized : p);
return await this.pupPage.evaluate(async (title, participants, options) => {
- const { messageTimer = 0, parentGroupId, autoSendInviteV4 = true, comment = '' } = options;
+ const {
+ messageTimer = 0,
+ parentGroupId,
+ autoSendInviteV4 = true,
+ comment = '',
+ } = options;
const participantData = {}, participantWids = [], failedParticipants = [];
let createGroupResult, parentGroupWid;
@@ -1586,7 +1668,9 @@ class Client extends EventEmitter {
for (const participant of participants) {
const pWid = window.Store.WidFactory.createWid(participant);
- if ((await window.Store.QueryExist(pWid))?.wid) participantWids.push(pWid);
+ if ((await window.Store.QueryExist(pWid))?.wid) {
+ participantWids.push({ phoneNumber: pWid });
+ }
else failedParticipants.push(participant);
}
@@ -1595,14 +1679,13 @@ class Client extends EventEmitter {
try {
createGroupResult = await window.Store.GroupUtils.createGroup(
{
- 'memberAddMode': options.memberAddMode === undefined ? true : options.memberAddMode,
- 'membershipApprovalMode': options.membershipApprovalMode === undefined ? false : options.membershipApprovalMode,
- 'announce': options.announce === undefined ? true : options.announce,
+ 'addressingModeOverride': 'lid',
+ 'memberAddMode': options.memberAddMode ?? false,
+ 'membershipApprovalMode': options.membershipApprovalMode ?? false,
+ 'announce': options.announce ?? false,
+ 'restrict': options.isRestrict !== undefined ? !options.isRestrict : false,
'ephemeralDuration': messageTimer,
- 'full': undefined,
'parentGroupId': parentGroupWid,
- 'restrict': options.restrict === undefined ? true : options.restrict,
- 'thumb': undefined,
'title': title,
},
participantWids
@@ -1613,13 +1696,14 @@ class Client extends EventEmitter {
for (const participant of createGroupResult.participants) {
let isInviteV4Sent = false;
+ participant.wid.server == 'lid' && (participant.wid = window.Store.LidUtils.getPhoneNumber(participant.wid));
const participantId = participant.wid._serialized;
const statusCode = participant.error || 200;
if (autoSendInviteV4 && statusCode === 403) {
window.Store.Contact.gadd(participant.wid, { silent: true });
const addParticipantResult = await window.Store.GroupInviteV4.sendGroupInviteMessage(
- await window.Store.Chat.find(participant.wid),
+ window.Store.Chat.get(participant.wid) || await window.Store.Chat.find(participant.wid),
createGroupResult.wid._serialized,
createGroupResult.subject,
participant.invite_code,
@@ -1627,9 +1711,7 @@ class Client extends EventEmitter {
comment,
await window.WWebJS.getProfilePicThumbToBase64(createGroupResult.wid)
);
- isInviteV4Sent = window.compareWwebVersions(window.Debug.VERSION, '<', '2.2335.6')
- ? addParticipantResult === 'OK'
- : addParticipantResult.messageSendResult === 'OK';
+ isInviteV4Sent = addParticipantResult.messageSendResult === 'OK';
}
participantData[participantId] = {
@@ -2179,6 +2261,45 @@ class Client extends EventEmitter {
}, chatId);
}
+ /**
+ * Generates a WhatsApp call link (video call or voice call)
+ * @param {Date} startTime The start time of the call
+ * @param {string} callType The type of a WhatsApp call link to generate, valid values are: `video` | `voice`
+ * @returns {Promise<string>} The WhatsApp call link (https://call.whatsapp.com/video/XxXxXxXxXxXxXx) or an empty string if a generation failed.
+ */
+ async createCallLink(startTime, callType) {
+ if (!['video', 'voice'].includes(callType)) {
+ throw new class CreateCallLinkError extends Error {
+ constructor(m) { super(m); }
+ }('Invalid \'callType\' parameter value is provided. Valid values are: \'voice\' | \'video\'.');
+ }
+
+ startTime = Math.floor(startTime.getTime() / 1000);
+
+ return await this.pupPage.evaluate(async (startTimeTs, callType) => {
+ const response = await window.Store.ScheduledEventMsgUtils.createEventCallLink(startTimeTs, callType);
+ return response ?? '';
+ }, startTime, callType);
+ }
+
+ /**
+ * Sends a response to the scheduled event message, indicating whether a user is going to attend the event or not
+ * @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
+ * @param {string} eventMessageId The scheduled event message ID
+ * @returns {Promise<boolean>}
+ */
+ async sendResponseToScheduledEvent(response, eventMessageId) {
+ if (![0, 1, 2, 3].includes(response)) return false;
+
+ return await this.pupPage.evaluate(async (response, msgId) => {
+ const eventMsg = window.Store.Msg.get(msgId) || (await window.Store.Msg.getMessagesById([msgId]))?.messages?.[0];
+ if (!eventMsg) return false;
+
+ await window.Store.ScheduledEventMsgUtils.sendEventResponseMsg(response, eventMsg);
+ return true;
+ }, response, eventMessageId);
+ }
+
/**
* Save new contact to user's addressbook or edit the existing one
* @param {string} phoneNumber The contact's phone number in a format "17182222222", where "1" is a country code
@@ -2211,6 +2332,28 @@ class Client extends EventEmitter {
return await window.Store.AddressbookContactUtils.deleteContactAction(phoneNumber);
}, phoneNumber);
}
+
+ /**
+ * Get lid and phone number for multiple users
+ * @param {string[]} userIds - Array of user IDs
+ * @returns {Promise<Array<{ lid: string, pn: string }>>}
+ */
+ async getContactLidAndPhone(userIds) {
+ return await this.pupPage.evaluate((userIds) => {
+ !Array.isArray(userIds) && (userIds = [userIds]);
+ return userIds.map(userId => {
+ const wid = window.Store.WidFactory.createWid(userId);
+ const isLid = wid.server === 'lid';
+ const lid = isLid ? wid : window.Store.LidUtils.getCurrentLid(wid);
+ const phone = isLid ? window.Store.LidUtils.getPhoneNumber(wid) : wid;
+
+ return {
+ lid: lid._serialized,
+ pn: phone._serialized
+ };
+ });
+ }, userIds);
+ }
}
module.exports = Client;
@@ -2224,7 +2367,7 @@ module.exports = Client;
diff --git a/docs/ClientInfo.html b/docs/ClientInfo.html
index 3b43d44..baa1bd3 100644
--- a/docs/ClientInfo.html
+++ b/docs/ClientInfo.html
@@ -4,7 +4,7 @@
- whatsapp-web.js 1.31.1-alpha.0 » Class: ClientInfo
+ whatsapp-web.js 1.32.0 » Class: ClientInfo
@@ -15,7 +15,7 @@
@@ -242,7 +242,7 @@
diff --git a/docs/Contact.html b/docs/Contact.html
index be258ba..065b7eb 100644
--- a/docs/Contact.html
+++ b/docs/Contact.html
@@ -4,7 +4,7 @@
- whatsapp-web.js 1.31.1-alpha.0 » Class: Contact
+ whatsapp-web.js 1.32.0 » Class: Contact
@@ -15,7 +15,7 @@
@@ -293,7 +293,7 @@
diff --git a/docs/GroupChat.html b/docs/GroupChat.html
index 525c97d..cd18797 100644
--- a/docs/GroupChat.html
+++ b/docs/GroupChat.html
@@ -4,7 +4,7 @@
- whatsapp-web.js 1.31.1-alpha.0 » Class: GroupChat
+ whatsapp-web.js 1.32.0 » Class: GroupChat
@@ -15,7 +15,7 @@
@@ -137,14 +137,17 @@