mirror of
https://github.com/cheveguerra/whatsapp-web.js.git
synced 2026-04-18 19:49:16 +00:00
Merge branch 'main' into patch-participants
This commit is contained in:
@@ -74,7 +74,7 @@ class Client extends EventEmitter {
|
||||
} else {
|
||||
browser = await puppeteer.launch(this.options.puppeteer);
|
||||
page = (await browser.pages())[0];
|
||||
}
|
||||
}
|
||||
|
||||
await page.setUserAgent(this.options.userAgent);
|
||||
|
||||
@@ -89,11 +89,13 @@ class Client extends EventEmitter {
|
||||
if (this.options.session) {
|
||||
await page.evaluateOnNewDocument(
|
||||
session => {
|
||||
localStorage.clear();
|
||||
localStorage.setItem('WABrowserId', session.WABrowserId);
|
||||
localStorage.setItem('WASecretBundle', session.WASecretBundle);
|
||||
localStorage.setItem('WAToken1', session.WAToken1);
|
||||
localStorage.setItem('WAToken2', session.WAToken2);
|
||||
if(document.referrer === 'https://whatsapp.com/') {
|
||||
localStorage.clear();
|
||||
localStorage.setItem('WABrowserId', session.WABrowserId);
|
||||
localStorage.setItem('WASecretBundle', session.WASecretBundle);
|
||||
localStorage.setItem('WAToken1', session.WAToken1);
|
||||
localStorage.setItem('WAToken2', session.WAToken2);
|
||||
}
|
||||
}, this.options.session);
|
||||
}
|
||||
|
||||
@@ -104,6 +106,7 @@ class Client extends EventEmitter {
|
||||
await page.goto(WhatsWebURL, {
|
||||
waitUntil: 'load',
|
||||
timeout: 0,
|
||||
referer: 'https://whatsapp.com/'
|
||||
});
|
||||
|
||||
const KEEP_PHONE_CONNECTED_IMG_SELECTOR = '[data-icon="intro-md-beta-logo-dark"], [data-icon="intro-md-beta-logo-light"], [data-asset-intro-image-light="true"], [data-asset-intro-image-dark="true"]';
|
||||
@@ -239,8 +242,6 @@ class Client extends EventEmitter {
|
||||
|
||||
// Register events
|
||||
await page.exposeFunction('onAddMessageEvent', msg => {
|
||||
if (!msg.isNewMsg) return;
|
||||
|
||||
if (msg.type === 'gp2') {
|
||||
const notification = new GroupNotification(this, msg);
|
||||
if (msg.subtype === 'add' || msg.subtype === 'invite') {
|
||||
@@ -425,7 +426,6 @@ class Client extends EventEmitter {
|
||||
});
|
||||
|
||||
await page.evaluate(() => {
|
||||
window.Store.Msg.on('add', (msg) => { if (msg.isNewMsg) window.onAddMessageEvent(window.WWebJS.getMessageModel(msg)); });
|
||||
window.Store.Msg.on('change', (msg) => { window.onChangeMessageEvent(window.WWebJS.getMessageModel(msg)); });
|
||||
window.Store.Msg.on('change:type', (msg) => { window.onChangeMessageTypeEvent(window.WWebJS.getMessageModel(msg)); });
|
||||
window.Store.Msg.on('change:ack', (msg,ack) => { window.onMessageAckEvent(window.WWebJS.getMessageModel(msg), ack); });
|
||||
@@ -434,6 +434,16 @@ class Client extends EventEmitter {
|
||||
window.Store.AppState.on('change:state', (_AppState, state) => { window.onAppStateChangedEvent(state); });
|
||||
window.Store.Conn.on('change:battery', (state) => { window.onBatteryStateChangedEvent(state); });
|
||||
window.Store.Call.on('add', (call) => { window.onIncomingCall(call); });
|
||||
window.Store.Msg.on('add', (msg) => {
|
||||
if (msg.isNewMsg) {
|
||||
if(msg.type === 'ciphertext') {
|
||||
// defer message event until ciphertext is resolved (type changed)
|
||||
msg.once('change:type', (_msg) => window.onAddMessageEvent(window.WWebJS.getMessageModel(_msg)));
|
||||
} else {
|
||||
window.onAddMessageEvent(window.WWebJS.getMessageModel(msg));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -445,7 +455,7 @@ class Client extends EventEmitter {
|
||||
// Disconnect when navigating away when in PAIRING state (detect logout)
|
||||
this.pupPage.on('framenavigated', async () => {
|
||||
const appState = await this.getState();
|
||||
if(appState === WAState.PAIRING) {
|
||||
if(!appState || appState === WAState.PAIRING) {
|
||||
this.emit(Events.DISCONNECTED, 'NAVIGATION');
|
||||
await this.destroy();
|
||||
}
|
||||
@@ -533,7 +543,7 @@ class Client extends EventEmitter {
|
||||
quotedMessageId: options.quotedMessageId,
|
||||
parseVCards: options.parseVCards === false ? false : true,
|
||||
mentionedJidList: Array.isArray(options.mentions) ? options.mentions.map(contact => contact.id._serialized) : [],
|
||||
...options.extra
|
||||
extraOptions: options.extra
|
||||
};
|
||||
|
||||
const sendSeen = typeof options.sendSeen === 'undefined' ? true : options.sendSeen;
|
||||
@@ -720,6 +730,7 @@ class Client extends EventEmitter {
|
||||
*/
|
||||
async getState() {
|
||||
return await this.pupPage.evaluate(() => {
|
||||
if(!window.Store) return null;
|
||||
return window.Store.AppState.state;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,13 +3,27 @@
|
||||
const MessageMedia = require('./MessageMedia');
|
||||
const Util = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Button spec used in Buttons constructor
|
||||
* @typedef {Object} ButtonSpec
|
||||
* @property {string=} id - Custom ID to set on the button. A random one will be generated if one is not passed.
|
||||
* @property {string} body - The text to show on the button.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} FormattedButtonSpec
|
||||
* @property {string} buttonId
|
||||
* @property {number} type
|
||||
* @property {Object} buttonText
|
||||
*/
|
||||
|
||||
/**
|
||||
* Message type buttons
|
||||
*/
|
||||
class Buttons {
|
||||
/**
|
||||
* @param {string|MessageMedia} body
|
||||
* @param {Array<Array<string>>} buttons
|
||||
* @param {ButtonSpec[]} buttons - See {@link ButtonSpec}
|
||||
* @param {string?} title
|
||||
* @param {string?} footer
|
||||
*/
|
||||
@@ -41,7 +55,7 @@ class Buttons {
|
||||
|
||||
/**
|
||||
* buttons of message
|
||||
* @type {Array<Array<string>>}
|
||||
* @type {FormattedButtonSpec[]}
|
||||
*/
|
||||
this.buttons = this._format(buttons);
|
||||
if(!this.buttons.length){ throw '[BT01] No buttons';}
|
||||
@@ -50,8 +64,8 @@ class Buttons {
|
||||
|
||||
/**
|
||||
* Creates button array from simple array
|
||||
* @param {Array<Array<string>>} buttons
|
||||
* @returns {Array<Array<string>>}
|
||||
* @param {ButtonSpec[]} buttons
|
||||
* @returns {FormattedButtonSpec[]}
|
||||
* @example
|
||||
* Input: [{id:'customId',body:'button1'},{body:'button2'},{body:'button3'},{body:'button4'}]
|
||||
* Returns: [{ buttonId:'customId',buttonText:{'displayText':'button1'},type: 1 },{buttonId:'n3XKsL',buttonText:{'displayText':'button2'},type:1},{buttonId:'NDJk0a',buttonText:{'displayText':'button3'},type:1}]
|
||||
|
||||
@@ -171,30 +171,32 @@ class Chat extends Base {
|
||||
/**
|
||||
* Loads chat messages, sorted from earliest to latest.
|
||||
* @param {Object} searchOptions Options for searching messages. Right now only limit is supported.
|
||||
* @param {Number} [searchOptions.limit=50] The amount of messages to return. Note that the actual number of returned messages may be smaller if there aren't enough messages in the conversation. Set this to Infinity to load all messages.
|
||||
* @param {Number} [searchOptions.limit] The amount of messages to return. If no limit is specified, the available messages will be returned. Note that the actual number of returned messages may be smaller if there aren't enough messages in the conversation. Set this to Infinity to load all messages.
|
||||
* @returns {Promise<Array<Message>>}
|
||||
*/
|
||||
async fetchMessages(searchOptions) {
|
||||
if (!searchOptions || !searchOptions.limit) {
|
||||
searchOptions = { limit: 50 };
|
||||
}
|
||||
let messages = await this.client.pupPage.evaluate(async (chatId, limit) => {
|
||||
let messages = await this.client.pupPage.evaluate(async (chatId, searchOptions) => {
|
||||
const msgFilter = m => !m.isNotification; // dont include notification messages
|
||||
|
||||
const chat = window.Store.Chat.get(chatId);
|
||||
let msgs = chat.msgs.models.filter(msgFilter);
|
||||
|
||||
while (msgs.length < limit) {
|
||||
const loadedMessages = await chat.loadEarlierMsgs();
|
||||
if (!loadedMessages) break;
|
||||
msgs = [...loadedMessages.filter(msgFilter), ...msgs];
|
||||
if (searchOptions && searchOptions.limit > 0) {
|
||||
while (msgs.length < searchOptions.limit) {
|
||||
const loadedMessages = await chat.loadEarlierMsgs();
|
||||
if (!loadedMessages) break;
|
||||
msgs = [...loadedMessages.filter(msgFilter), ...msgs];
|
||||
}
|
||||
|
||||
if (msgs.length > searchOptions.limit) {
|
||||
msgs.sort((a, b) => (a.t > b.t) ? 1 : -1);
|
||||
msgs = msgs.splice(msgs.length - searchOptions.limit);
|
||||
}
|
||||
}
|
||||
|
||||
msgs.sort((a, b) => (a.t > b.t) ? 1 : -1);
|
||||
if (msgs.length > limit) msgs = msgs.splice(msgs.length - limit);
|
||||
return msgs.map(m => window.WWebJS.getMessageModel(m));
|
||||
|
||||
}, this.id._serialized, searchOptions.limit);
|
||||
}, this.id._serialized, searchOptions);
|
||||
|
||||
return messages.map(m => new Message(this.client, m));
|
||||
}
|
||||
|
||||
@@ -185,6 +185,12 @@ class Message extends Base {
|
||||
*/
|
||||
this.isGif = Boolean(data.isGif);
|
||||
|
||||
/**
|
||||
* Indicates if the message will disappear after it expires
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.isEphemeral = data.isEphemeral;
|
||||
|
||||
/** Title */
|
||||
if (data.title) {
|
||||
this.title = data.title;
|
||||
|
||||
@@ -77,8 +77,25 @@ exports.MessageTypes = {
|
||||
UNKNOWN: 'unknown',
|
||||
GROUP_INVITE: 'groups_v4_invite',
|
||||
LIST: 'list',
|
||||
LIST_RESPONSE: 'list_response',
|
||||
BUTTONS_RESPONSE: 'buttons_response',
|
||||
PAYMENT: 'payment'
|
||||
PAYMENT: 'payment',
|
||||
BROADCAST_NOTIFICATION: 'broadcast_notification',
|
||||
CALL_LOG: 'call_log',
|
||||
CIPHERTEXT: 'ciphertext',
|
||||
DEBUG: 'debug',
|
||||
E2E_NOTIFICATION: 'e2e_notification',
|
||||
GP2: 'gp2',
|
||||
GROUP_NOTIFICATION: 'group_notification',
|
||||
HSM: 'hsm',
|
||||
INTERACTIVE: 'interactive',
|
||||
NATIVE_FLOW: 'native_flow',
|
||||
NOTIFICATION: 'notification',
|
||||
NOTIFICATION_TEMPLATE: 'notification_template',
|
||||
OVERSIZED: 'oversized',
|
||||
PROTOCOL: 'protocol',
|
||||
REACTION: 'reaction',
|
||||
TEMPLATE_BUTTON_REPLY: 'template_button_reply',
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -153,7 +153,7 @@ exports.LoadUtils = () => {
|
||||
}
|
||||
}
|
||||
|
||||
let extraOptions = {};
|
||||
let buttonOptions = {};
|
||||
if(options.buttons){
|
||||
let caption;
|
||||
if(options.buttons.type === 'chat') {
|
||||
@@ -162,7 +162,7 @@ exports.LoadUtils = () => {
|
||||
}else{
|
||||
caption = options.caption ? options.caption : ' '; //Caption can't be empty
|
||||
}
|
||||
extraOptions = {
|
||||
buttonOptions = {
|
||||
productHeaderImageRejected: false,
|
||||
isFromTemplate: false,
|
||||
isDynamicReplyButtonsMsg: true,
|
||||
@@ -175,12 +175,12 @@ exports.LoadUtils = () => {
|
||||
delete options.buttons;
|
||||
}
|
||||
|
||||
let listOptions = {};
|
||||
if(options.list){
|
||||
if(window.Store.Conn.platform === 'smba' || window.Store.Conn.platform === 'smbi'){
|
||||
throw '[LT01] Whatsapp business can\'t send this yet';
|
||||
}
|
||||
extraOptions = {
|
||||
...extraOptions,
|
||||
listOptions = {
|
||||
type: 'list',
|
||||
footer: options.list.footer,
|
||||
list: {
|
||||
@@ -190,7 +190,7 @@ exports.LoadUtils = () => {
|
||||
body: options.list.description
|
||||
};
|
||||
delete options.list;
|
||||
delete extraOptions.list.footer;
|
||||
delete listOptions.list.footer;
|
||||
}
|
||||
|
||||
const newMsgId = new window.Store.MsgKey({
|
||||
@@ -199,6 +199,15 @@ exports.LoadUtils = () => {
|
||||
id: window.Store.genId(),
|
||||
});
|
||||
|
||||
const extraOptions = options.extraOptions || {};
|
||||
delete options.extraOptions;
|
||||
|
||||
const ephemeralSettings = {
|
||||
ephemeralDuration: chat.isEphemeralSettingOn() ? chat.getEphemeralSetting() : undefined,
|
||||
ephemeralSettingTimestamp: chat.getEphemeralSettingTimestamp() || undefined,
|
||||
disappearingModeInitiator: chat.getDisappearingModeInitiator() || undefined,
|
||||
};
|
||||
|
||||
const message = {
|
||||
...options,
|
||||
id: newMsgId,
|
||||
@@ -211,10 +220,13 @@ exports.LoadUtils = () => {
|
||||
t: parseInt(new Date().getTime() / 1000),
|
||||
isNewMsg: true,
|
||||
type: 'chat',
|
||||
...ephemeralSettings,
|
||||
...locationOptions,
|
||||
...attOptions,
|
||||
...quotedMsgOptions,
|
||||
...vcardOptions,
|
||||
...buttonOptions,
|
||||
...listOptions,
|
||||
...extraOptions
|
||||
};
|
||||
|
||||
@@ -313,6 +325,7 @@ exports.LoadUtils = () => {
|
||||
window.WWebJS.getMessageModel = message => {
|
||||
const msg = message.serialize();
|
||||
|
||||
msg.isEphemeral = message.isEphemeral;
|
||||
msg.isStatusV3 = message.isStatusV3;
|
||||
msg.links = (message.getLinks()).map(link => ({
|
||||
link: link.href,
|
||||
|
||||
Reference in New Issue
Block a user