Merge branch 'main' into patch-participants

This commit is contained in:
Rajeh Taher
2022-02-09 10:57:41 +02:00
committed by GitHub
57 changed files with 1009 additions and 273 deletions

View File

@@ -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;
});
}

View File

@@ -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}]

View File

@@ -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));
}

View File

@@ -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;

View File

@@ -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',
};
/**

View File

@@ -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,