Farewell, Webpack! Re-injection, Pairing Code login, and 2.24x jumbofix (#2816)
* fix(webpack-exodus): Added the new patch * fix(webpack-exodus): Fixed v2.3000.x and v2.24 * chore(webpack-exodus): eslint * fix(webpack-exodus): Fixed Store * fix(webpack-exodus): slight store fix * chore(webpack-exodus): fix example so that I don't crash on every load * chore: eslint fix * fix(webpack-exodus): prevent confusion with build systems * fix(webpack-exodus): Add proper groups module * fix(webpack-exodus): Fix order queries * fix(webpack-exodus): Bad export name * feat(webpack-exodus): Re-injection, No more selectors, Pairing Code auth * fix(webpack-exodus): Eslint and better login/logout handling * fix(webpack-exodus): 2.24 compatibility * revert testing options * chore(webpack-exodus): ESLint * chore(webpack-exodus): final eslint tweak (did not commit with last commit for some reason) * style: fix broken link in readme file * fix(webpack-exodus): Fix forwarding messages * fix(webpack-exodus): finish forwarding function * Making the ESLint god happy * Fix window.Store.ProfilePic.profilePicFind is not a function error (#3001) * Change window.Store.ProfilePic.profilePicFind to window.Store.ProfilePic.requestProfilePicFromServer * Prevent breaking v2.2x * refactor: make it more readable --------- Co-authored-by: alechkos <93551621+alechkos@users.noreply.github.com> * Sanitize, improves and fixes * make ESList god happy * fix getInviteCode for group (#3007) (#3029) * fix getInviteCode for group (#3007) * move changes to proper getInviteCode method. * style fix * refactor: add try-catch in a case the user is not a group admin * refactor: handle promise properly --------- Co-authored-by: Sven Neumann <s.neumann@websix.de> Co-authored-by: alechkos <93551621+alechkos@users.noreply.github.com> * fixes * Update LocalWebCache.js * fix delete on 2.3000 (#3048) * fix delete on 2.3000 * fix compareWwebVersions * fix: use proper comparison operator --------- Co-authored-by: alechkos <93551621+alechkos@users.noreply.github.com> * Implement bot invoking capabilities (#3009) * Implement bot invoking capabilities * Update index.d.ts * Eslint * Webpack exodus fix message_reaction event (#3099) * exposed WAWebAddonReactionTableMode * capture reaction using WAWebAddonReactionTableMode * move compareWwebVersions and check version before trying to expose new store * message_reaction on example.js (#3102) * handle message_reaction event on example.js * log wweb version on ready * fix reactions on pvt chats (#3104) * New group function * new group settings * Update Client.js --------- Co-authored-by: alechkos <93551621+alechkos@users.noreply.github.com> Co-authored-by: tuyuribr <45042245+tuyuribr@users.noreply.github.com> Co-authored-by: Seow Zhen Jun <56989187+seowzhenjun0126@users.noreply.github.com> Co-authored-by: Sven <sven2neumann@gmail.com> Co-authored-by: Sven Neumann <s.neumann@websix.de> Co-authored-by: Jorge Rocha Gualtieri <jorge@jrg.com.br> Co-authored-by: Mateus Mercer <accounts@matbm.net>
This commit is contained in:
parent
24b11b7d3f
commit
13ce571c64
14 changed files with 986 additions and 777 deletions
33
example.js
33
example.js
|
|
@ -5,19 +5,30 @@ const client = new Client({
|
|||
// proxyAuthentication: { username: 'username', password: 'password' },
|
||||
puppeteer: {
|
||||
// args: ['--proxy-server=proxy-server-that-requires-authentication.example.com'],
|
||||
headless: false
|
||||
headless: false,
|
||||
}
|
||||
});
|
||||
|
||||
// client initialize does not finish at ready now.
|
||||
client.initialize();
|
||||
|
||||
client.on('loading_screen', (percent, message) => {
|
||||
console.log('LOADING SCREEN', percent, message);
|
||||
});
|
||||
|
||||
client.on('qr', (qr) => {
|
||||
// Pairing code only needs to be requested once
|
||||
let pairingCodeRequested = false;
|
||||
client.on('qr', async (qr) => {
|
||||
// NOTE: This event will not be fired if a session is specified.
|
||||
console.log('QR RECEIVED', qr);
|
||||
|
||||
// paiuting code example
|
||||
const pairingCodeEnabled = false;
|
||||
if (pairingCodeEnabled && !pairingCodeRequested) {
|
||||
const pairingCode = await client.requestPairingCode('96170100100'); // enter the target phone number
|
||||
console.log('Pairing code enabled, code: '+ pairingCode);
|
||||
pairingCodeRequested = true;
|
||||
}
|
||||
});
|
||||
|
||||
client.on('authenticated', () => {
|
||||
|
|
@ -29,8 +40,18 @@ client.on('auth_failure', msg => {
|
|||
console.error('AUTHENTICATION FAILURE', msg);
|
||||
});
|
||||
|
||||
client.on('ready', () => {
|
||||
client.on('ready', async () => {
|
||||
console.log('READY');
|
||||
const debugWWebVersion = await client.getWWebVersion();
|
||||
console.log(`WWebVersion = ${debugWWebVersion}`);
|
||||
|
||||
client.pupPage.on('pageerror', function(err) {
|
||||
console.log('Page error: ' + err.toString());
|
||||
});
|
||||
client.pupPage.on('error', function(err) {
|
||||
console.log('Page error: ' + err.toString());
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
client.on('message', async msg => {
|
||||
|
|
@ -418,7 +439,7 @@ client.on('message', async msg => {
|
|||
requesterIds: ['number1@c.us', 'number2@c.us'],
|
||||
sleep: null
|
||||
});
|
||||
} else {
|
||||
} else if (msg.body === '!pinmsg') {
|
||||
/**
|
||||
* Pins a message in a chat, a method takes a number in seconds for the message to be pinned.
|
||||
* WhatsApp default values for duration to pass to the method are:
|
||||
|
|
@ -595,6 +616,10 @@ client.on('group_membership_request', async (notification) => {
|
|||
await client.rejectGroupMembershipRequests(notification.chatId, notification.author);
|
||||
});
|
||||
|
||||
client.on('message_reaction', async (reaction) => {
|
||||
console.log('REACTION RECEIVED', reaction);
|
||||
});
|
||||
|
||||
client.on('vote_update', (vote) => {
|
||||
/** The vote that was affected: */
|
||||
console.log(vote);
|
||||
|
|
|
|||
16
index.d.ts
vendored
16
index.d.ts
vendored
|
|
@ -2,6 +2,7 @@
|
|||
import { EventEmitter } from 'events'
|
||||
import { RequestInit } from 'node-fetch'
|
||||
import * as puppeteer from 'puppeteer'
|
||||
import InterfaceController from './src/util/InterfaceController'
|
||||
|
||||
declare namespace WAWebJS {
|
||||
|
||||
|
|
@ -17,6 +18,9 @@ declare namespace WAWebJS {
|
|||
/** Puppeteer browser running WhatsApp Web */
|
||||
pupBrowser?: puppeteer.Browser
|
||||
|
||||
/** Client interactivity interface */
|
||||
interface?: InterfaceController
|
||||
|
||||
/**Accepts an invitation to join a group */
|
||||
acceptInvite(inviteCode: string): Promise<string>
|
||||
|
||||
|
|
@ -111,6 +115,14 @@ declare namespace WAWebJS {
|
|||
*/
|
||||
muteChat(chatId: string, unmuteDate?: Date): Promise<void>
|
||||
|
||||
/**
|
||||
* Request authentication via pairing code instead of QR code
|
||||
* @param phoneNumber - Phone number in international, symbol-free format (e.g. 12025550108 for US, 551155501234 for Brazil)
|
||||
* @param showNotification - Show notification to pair on phone number
|
||||
* @returns {Promise<string>} - Returns a pairing code in format "ABCDEFGH"
|
||||
*/
|
||||
requestPairingCode(phoneNumber: string, showNotification = true): Promise<string>
|
||||
|
||||
/** Force reset of connection state for the client */
|
||||
resetState(): Promise<void>
|
||||
|
||||
|
|
@ -215,7 +227,7 @@ declare namespace WAWebJS {
|
|||
/** Emitted when the client has been disconnected */
|
||||
on(event: 'disconnected', listener: (
|
||||
/** reason that caused the disconnect */
|
||||
reason: WAState | "NAVIGATION"
|
||||
reason: WAState | "LOGOUT"
|
||||
) => void): this
|
||||
|
||||
/** Emitted when a user joins the chat via invite link or is added by an admin */
|
||||
|
|
@ -1097,6 +1109,8 @@ declare namespace WAWebJS {
|
|||
}[]
|
||||
/** Send 'seen' status */
|
||||
sendSeen?: boolean
|
||||
/** Bot Wid when doing a bot mention like @Meta AI */
|
||||
invokedBotWid?: string
|
||||
/** Media to be sent */
|
||||
media?: MessageMedia
|
||||
/** Extra options */
|
||||
|
|
|
|||
1
index.js
1
index.js
|
|
@ -27,7 +27,6 @@ module.exports = {
|
|||
NoAuth: require('./src/authStrategies/NoAuth'),
|
||||
LocalAuth: require('./src/authStrategies/LocalAuth'),
|
||||
RemoteAuth: require('./src/authStrategies/RemoteAuth'),
|
||||
LegacySessionAuth: require('./src/authStrategies/LegacySessionAuth'),
|
||||
|
||||
...Constants
|
||||
};
|
||||
|
|
|
|||
578
src/Client.js
578
src/Client.js
|
|
@ -7,12 +7,15 @@ const moduleRaid = require('@pedroslopez/moduleraid/moduleraid');
|
|||
const Util = require('./util/Util');
|
||||
const InterfaceController = require('./util/InterfaceController');
|
||||
const { WhatsWebURL, DefaultOptions, Events, WAState } = require('./util/Constants');
|
||||
const { ExposeStore, LoadUtils } = require('./util/Injected');
|
||||
const { ExposeAuthStore } = require('./util/Injected/AuthStore/AuthStore');
|
||||
const { ExposeStore } = require('./util/Injected/Store');
|
||||
const { ExposeLegacyAuthStore } = require('./util/Injected/AuthStore/LegacyAuthStore');
|
||||
const { ExposeLegacyStore } = require('./util/Injected/LegacyStore');
|
||||
const { LoadUtils } = require('./util/Injected/Utils');
|
||||
const ChatFactory = require('./factories/ChatFactory');
|
||||
const ContactFactory = require('./factories/ContactFactory');
|
||||
const WebCacheFactory = require('./webCache/WebCacheFactory');
|
||||
const { ClientInfo, Message, MessageMedia, Contact, Location, Poll, PollVote, GroupNotification, Label, Call, Buttons, List, Reaction } = require('./structures');
|
||||
const LegacySessionAuth = require('./authStrategies/LegacySessionAuth');
|
||||
const NoAuth = require('./authStrategies/NoAuth');
|
||||
|
||||
/**
|
||||
|
|
@ -63,37 +66,227 @@ class Client extends EventEmitter {
|
|||
this.options = Util.mergeDefault(DefaultOptions, options);
|
||||
|
||||
if(!this.options.authStrategy) {
|
||||
if(Object.prototype.hasOwnProperty.call(this.options, 'session')) {
|
||||
process.emitWarning(
|
||||
'options.session is deprecated and will be removed in a future release due to incompatibility with multi-device. ' +
|
||||
'Use the LocalAuth authStrategy, don\'t pass in a session as an option, or suppress this warning by using the LegacySessionAuth strategy explicitly (see https://wwebjs.dev/guide/authentication.html#legacysessionauth-strategy).',
|
||||
'DeprecationWarning'
|
||||
);
|
||||
|
||||
this.authStrategy = new LegacySessionAuth({
|
||||
session: this.options.session,
|
||||
restartOnAuthFail: this.options.restartOnAuthFail
|
||||
});
|
||||
} else {
|
||||
this.authStrategy = new NoAuth();
|
||||
}
|
||||
} else {
|
||||
this.authStrategy = this.options.authStrategy;
|
||||
}
|
||||
|
||||
this.authStrategy.setup(this);
|
||||
|
||||
/**
|
||||
* @type {puppeteer.Browser}
|
||||
*/
|
||||
this.pupBrowser = null;
|
||||
/**
|
||||
* @type {puppeteer.Page}
|
||||
*/
|
||||
this.pupPage = null;
|
||||
|
||||
this.currentIndexHtml = null;
|
||||
this.lastLoggedOut = false;
|
||||
|
||||
Util.setFfmpegPath(this.options.ffmpegPath);
|
||||
}
|
||||
/**
|
||||
* Injection logic
|
||||
* Private function
|
||||
* @property {boolean} reinject is this a reinject?
|
||||
*/
|
||||
async inject(reinject = false) {
|
||||
await this.pupPage.waitForFunction('window.Debug?.VERSION != undefined', {timeout: this.options.authTimeoutMs});
|
||||
|
||||
const version = await this.getWWebVersion();
|
||||
const isCometOrAbove = parseInt(version.split('.')?.[1]) >= 3000;
|
||||
|
||||
if (isCometOrAbove) {
|
||||
await this.pupPage.evaluate(ExposeAuthStore);
|
||||
} else {
|
||||
await this.pupPage.evaluate(ExposeLegacyAuthStore, moduleRaid.toString());
|
||||
}
|
||||
|
||||
const needAuthentication = await this.pupPage.evaluate(async () => {
|
||||
let state = window.AuthStore.AppState.state;
|
||||
|
||||
if (state === 'OPENING' || state === 'UNLAUNCHED' || state === 'PAIRING') {
|
||||
// wait till state changes
|
||||
await new Promise(r => {
|
||||
window.AuthStore.AppState.on('change:state', function waitTillInit(_AppState, state) {
|
||||
if (state !== 'OPENING' && state !== 'UNLAUNCHED' && state !== 'PAIRING') {
|
||||
window.AuthStore.AppState.off('change:state', waitTillInit);
|
||||
r();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
state = window.AuthStore.AppState.state;
|
||||
return state == 'UNPAIRED' || state == 'UNPAIRED_IDLE';
|
||||
});
|
||||
|
||||
if (needAuthentication) {
|
||||
const { failed, failureEventPayload, restart } = await this.authStrategy.onAuthenticationNeeded();
|
||||
|
||||
if(failed) {
|
||||
/**
|
||||
* Emitted when there has been an error while trying to restore an existing session
|
||||
* @event Client#auth_failure
|
||||
* @param {string} message
|
||||
*/
|
||||
this.emit(Events.AUTHENTICATION_FAILURE, failureEventPayload);
|
||||
await this.destroy();
|
||||
if (restart) {
|
||||
// session restore failed so try again but without session to force new authentication
|
||||
return this.initialize();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Register qr events
|
||||
let qrRetries = 0;
|
||||
const injected = await this.pupPage.evaluate(() => {
|
||||
return typeof window.onQRChangedEvent !== 'undefined';
|
||||
});
|
||||
if (!injected) {
|
||||
await this.pupPage.exposeFunction('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
|
||||
});
|
||||
}
|
||||
|
||||
if (!reinject) {
|
||||
await this.pupPage.exposeFunction('onAuthAppStateChangedEvent', async (state) => {
|
||||
if (state == 'UNPAIRED_IDLE') {
|
||||
// refresh qr code
|
||||
window.Store.Cmd.refreshQR();
|
||||
}
|
||||
});
|
||||
|
||||
await this.pupPage.exposeFunction('onAppStateHasSyncedEvent', async () => {
|
||||
const authEventPayload = await this.authStrategy.getAuthEventPayload();
|
||||
/**
|
||||
* Emitted when authentication is successful
|
||||
* @event Client#authenticated
|
||||
*/
|
||||
this.emit(Events.AUTHENTICATED, authEventPayload);
|
||||
|
||||
const injected = await this.pupPage.evaluate(async () => {
|
||||
return typeof window.Store !== 'undefined' && typeof window.WWebJS !== 'undefined';
|
||||
});
|
||||
|
||||
if (!injected) {
|
||||
if (this.options.webVersionCache.type === 'local' && this.currentIndexHtml) {
|
||||
const { type: webCacheType, ...webCacheOptions } = this.options.webVersionCache;
|
||||
const webCache = WebCacheFactory.createWebCache(webCacheType, webCacheOptions);
|
||||
|
||||
await webCache.persist(this.currentIndexHtml, version);
|
||||
}
|
||||
|
||||
if (isCometOrAbove) {
|
||||
await this.pupPage.evaluate(ExposeStore);
|
||||
} else {
|
||||
// make sure all modules are ready before injection
|
||||
// 2 second delay after authentication makes sense and does not need to be made dyanmic or removed
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
await this.pupPage.evaluate(ExposeLegacyStore);
|
||||
}
|
||||
|
||||
// Check window.Store Injection
|
||||
await this.pupPage.waitForFunction('window.Store != undefined');
|
||||
|
||||
/**
|
||||
* Current connection information
|
||||
* @type {ClientInfo}
|
||||
*/
|
||||
this.info = new ClientInfo(this, await this.pupPage.evaluate(() => {
|
||||
return { ...window.Store.Conn.serialize(), wid: window.Store.User.getMeUser() };
|
||||
}));
|
||||
|
||||
this.interface = new InterfaceController(this);
|
||||
|
||||
//Load util functions (serializers, helper functions)
|
||||
await this.pupPage.evaluate(LoadUtils);
|
||||
|
||||
await this.attachEventListeners(reinject);
|
||||
reinject = true;
|
||||
}
|
||||
/**
|
||||
* Emitted when the client has initialized and is ready to receive messages.
|
||||
* @event Client#ready
|
||||
*/
|
||||
this.emit(Events.READY);
|
||||
this.authStrategy.afterAuthReady();
|
||||
});
|
||||
let lastPercent = null;
|
||||
await this.pupPage.exposeFunction('onOfflineProgressUpdateEvent', async (percent) => {
|
||||
if (lastPercent !== percent) {
|
||||
lastPercent = percent;
|
||||
this.emit(Events.LOADING_SCREEN, percent, 'WhatsApp'); // Message is hardcoded as "WhatsApp" for now
|
||||
}
|
||||
});
|
||||
}
|
||||
const logoutCatchInjected = await this.pupPage.evaluate(() => {
|
||||
return typeof window.onLogoutEvent !== 'undefined';
|
||||
});
|
||||
if (!logoutCatchInjected) {
|
||||
await this.pupPage.exposeFunction('onLogoutEvent', async () => {
|
||||
this.lastLoggedOut = true;
|
||||
await this.pupPage.waitForNavigation({waitUntil: 'load', timeout: 5000}).catch((_) => _);
|
||||
});
|
||||
}
|
||||
await this.pupPage.evaluate(() => {
|
||||
window.AuthStore.AppState.on('change:state', (_AppState, state) => { window.onAuthAppStateChangedEvent(state); });
|
||||
window.AuthStore.AppState.on('change:hasSynced', () => { window.onAppStateHasSyncedEvent(); });
|
||||
window.AuthStore.Cmd.on('offline_progress_update', () => {
|
||||
window.onOfflineProgressUpdateEvent(window.AuthStore.OfflineMessageHandler.getOfflineDeliveryProgress());
|
||||
});
|
||||
window.AuthStore.Cmd.on('logout', async () => {
|
||||
await window.onLogoutEvent();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up events and requirements, kicks off authentication request
|
||||
*/
|
||||
async initialize() {
|
||||
let [browser, page] = [null, null];
|
||||
|
||||
let
|
||||
/**
|
||||
* @type {puppeteer.Browser}
|
||||
*/
|
||||
browser,
|
||||
/**
|
||||
* @type {puppeteer.Page}
|
||||
*/
|
||||
page;
|
||||
|
||||
browser = null;
|
||||
page = null;
|
||||
|
||||
await this.authStrategy.beforeBrowserInitialized();
|
||||
|
||||
|
|
@ -126,7 +319,8 @@ class Client extends EventEmitter {
|
|||
await this.authStrategy.afterBrowserInitialized();
|
||||
await this.initWebVersionCache();
|
||||
|
||||
// ocVesion (isOfficialClient patch)
|
||||
// ocVersion (isOfficialClient patch)
|
||||
// remove after 2.3000.x hard release
|
||||
await page.evaluateOnNewDocument(() => {
|
||||
const originalError = Error;
|
||||
//eslint-disable-next-line no-global-assign
|
||||
|
|
@ -144,236 +338,42 @@ class Client extends EventEmitter {
|
|||
referer: 'https://whatsapp.com/'
|
||||
});
|
||||
|
||||
await page.evaluate(`function getElementByXpath(path) {
|
||||
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||
}`);
|
||||
await this.inject();
|
||||
|
||||
let lastPercent = null,
|
||||
lastPercentMessage = null;
|
||||
|
||||
await page.exposeFunction('loadingScreen', async (percent, message) => {
|
||||
if (lastPercent !== percent || lastPercentMessage !== message) {
|
||||
this.emit(Events.LOADING_SCREEN, percent, message);
|
||||
lastPercent = percent;
|
||||
lastPercentMessage = message;
|
||||
this.pupPage.on('framenavigated', async (frame) => {
|
||||
if(frame.url().includes('post_logout=1') || this.lastLoggedOut) {
|
||||
this.emit(Events.DISCONNECTED, 'LOGOUT');
|
||||
await this.authStrategy.logout();
|
||||
await this.authStrategy.beforeBrowserInitialized();
|
||||
await this.authStrategy.afterBrowserInitialized();
|
||||
this.lastLoggedOut = false;
|
||||
}
|
||||
await this.inject(true);
|
||||
});
|
||||
|
||||
await page.evaluate(
|
||||
async function (selectors) {
|
||||
var observer = new MutationObserver(function () {
|
||||
let progressBar = window.getElementByXpath(
|
||||
selectors.PROGRESS
|
||||
);
|
||||
let progressMessage = window.getElementByXpath(
|
||||
selectors.PROGRESS_MESSAGE
|
||||
);
|
||||
|
||||
if (progressBar) {
|
||||
window.loadingScreen(
|
||||
progressBar.value,
|
||||
progressMessage.innerText
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
characterData: true,
|
||||
subtree: true,
|
||||
});
|
||||
},
|
||||
{
|
||||
PROGRESS: '//*[@id=\'app\']/div/div/div[2]/progress',
|
||||
PROGRESS_MESSAGE: '//*[@id=\'app\']/div/div/div[3]',
|
||||
}
|
||||
);
|
||||
|
||||
const INTRO_IMG_SELECTOR = '[data-icon=\'search\']';
|
||||
const INTRO_QRCODE_SELECTOR = 'div[data-ref] canvas';
|
||||
|
||||
// Checks which selector appears first
|
||||
const needAuthentication = await Promise.race([
|
||||
new Promise(resolve => {
|
||||
page.waitForSelector(INTRO_IMG_SELECTOR, { timeout: this.options.authTimeoutMs })
|
||||
.then(() => resolve(false))
|
||||
.catch((err) => resolve(err));
|
||||
}),
|
||||
new Promise(resolve => {
|
||||
page.waitForSelector(INTRO_QRCODE_SELECTOR, { timeout: this.options.authTimeoutMs })
|
||||
.then(() => resolve(true))
|
||||
.catch((err) => resolve(err));
|
||||
})
|
||||
]);
|
||||
|
||||
// Checks if an error occurred on the first found selector. The second will be discarded and ignored by .race;
|
||||
if (needAuthentication instanceof Error) throw needAuthentication;
|
||||
|
||||
// Scan-qrcode selector was found. Needs authentication
|
||||
if (needAuthentication) {
|
||||
const { failed, failureEventPayload, restart } = await this.authStrategy.onAuthenticationNeeded();
|
||||
if(failed) {
|
||||
/**
|
||||
* Emitted when there has been an error while trying to restore an existing session
|
||||
* @event Client#auth_failure
|
||||
* @param {string} message
|
||||
*/
|
||||
this.emit(Events.AUTHENTICATION_FAILURE, failureEventPayload);
|
||||
await this.destroy();
|
||||
if (restart) {
|
||||
// session restore failed so try again but without session to force new authentication
|
||||
return this.initialize();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const QR_CONTAINER = 'div[data-ref]';
|
||||
const QR_RETRY_BUTTON = 'div[data-ref] > span > button';
|
||||
let qrRetries = 0;
|
||||
await page.exposeFunction('qrChanged', 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 page.evaluate(function (selectors) {
|
||||
const qr_container = document.querySelector(selectors.QR_CONTAINER);
|
||||
window.qrChanged(qr_container.dataset.ref);
|
||||
|
||||
const obs = new MutationObserver((muts) => {
|
||||
muts.forEach(mut => {
|
||||
// Listens to qr token change
|
||||
if (mut.type === 'attributes' && mut.attributeName === 'data-ref') {
|
||||
window.qrChanged(mut.target.dataset.ref);
|
||||
}
|
||||
// Listens to retry button, when found, click it
|
||||
else if (mut.type === 'childList') {
|
||||
const retry_button = document.querySelector(selectors.QR_RETRY_BUTTON);
|
||||
if (retry_button) retry_button.click();
|
||||
}
|
||||
});
|
||||
});
|
||||
obs.observe(qr_container.parentElement, {
|
||||
subtree: true,
|
||||
childList: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['data-ref'],
|
||||
});
|
||||
}, {
|
||||
QR_CONTAINER,
|
||||
QR_RETRY_BUTTON
|
||||
});
|
||||
|
||||
// Wait for code scan
|
||||
try {
|
||||
await page.waitForSelector(INTRO_IMG_SELECTOR, { timeout: 0 });
|
||||
} catch(error) {
|
||||
if (
|
||||
error.name === 'ProtocolError' &&
|
||||
error.message &&
|
||||
error.message.match(/Target closed/)
|
||||
) {
|
||||
// something has called .destroy() while waiting
|
||||
return;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await page.evaluate(() => {
|
||||
/**
|
||||
* Helper function that compares between two WWeb versions. Its purpose is to help the developer to choose the correct code implementation depending on the comparison value and the WWeb version.
|
||||
* @param {string} lOperand The left operand for the WWeb version string to compare with
|
||||
* @param {string} operator The comparison operator
|
||||
* @param {string} rOperand The right operand for the WWeb version string to compare with
|
||||
* @returns {boolean} Boolean value that indicates the result of the comparison
|
||||
*/
|
||||
window.compareWwebVersions = (lOperand, operator, rOperand) => {
|
||||
if (!['>', '>=', '<', '<=', '='].includes(operator)) {
|
||||
throw new class _ extends Error {
|
||||
constructor(m) { super(m); this.name = 'CompareWwebVersionsError'; }
|
||||
}('Invalid comparison operator is provided');
|
||||
|
||||
}
|
||||
if (typeof lOperand !== 'string' || typeof rOperand !== 'string') {
|
||||
throw new class _ extends Error {
|
||||
constructor(m) { super(m); this.name = 'CompareWwebVersionsError'; }
|
||||
}('A non-string WWeb version type is provided');
|
||||
}
|
||||
|
||||
lOperand = lOperand.replace(/-beta$/, '');
|
||||
rOperand = rOperand.replace(/-beta$/, '');
|
||||
|
||||
while (lOperand.length !== rOperand.length) {
|
||||
lOperand.length > rOperand.length
|
||||
? rOperand = rOperand.concat('0')
|
||||
: lOperand = lOperand.concat('0');
|
||||
}
|
||||
|
||||
lOperand = Number(lOperand.replace(/\./g, ''));
|
||||
rOperand = Number(rOperand.replace(/\./g, ''));
|
||||
|
||||
return (
|
||||
operator === '>' ? lOperand > rOperand :
|
||||
operator === '>=' ? lOperand >= rOperand :
|
||||
operator === '<' ? lOperand < rOperand :
|
||||
operator === '<=' ? lOperand <= rOperand :
|
||||
operator === '=' ? lOperand === rOperand :
|
||||
false
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
await page.evaluate(ExposeStore, moduleRaid.toString());
|
||||
const authEventPayload = await this.authStrategy.getAuthEventPayload();
|
||||
|
||||
/**
|
||||
* Emitted when authentication is successful
|
||||
* @event Client#authenticated
|
||||
* 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
|
||||
* @returns {Promise<string>} - Returns a pairing code in format "ABCDEFGH"
|
||||
*/
|
||||
this.emit(Events.AUTHENTICATED, authEventPayload);
|
||||
|
||||
// Check window.Store Injection
|
||||
await page.waitForFunction('window.Store != undefined');
|
||||
|
||||
await page.evaluate(async () => {
|
||||
// safely unregister service workers
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
for (let registration of registrations) {
|
||||
registration.unregister();
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
//Load util functions (serializers, helper functions)
|
||||
await page.evaluate(LoadUtils);
|
||||
|
||||
// Expose client info
|
||||
/**
|
||||
* Current connection information
|
||||
* @type {ClientInfo}
|
||||
* Attach event listeners to WA Web
|
||||
* Private function
|
||||
* @property {boolean} reinject is this a reinject?
|
||||
*/
|
||||
this.info = new ClientInfo(this, await page.evaluate(() => {
|
||||
return { ...window.Store.Conn.serialize(), wid: window.Store.User.getMeUser() };
|
||||
}));
|
||||
|
||||
// Add InterfaceController
|
||||
this.interface = new InterfaceController(this);
|
||||
|
||||
// Register events
|
||||
await page.exposeFunction('onAddMessageEvent', msg => {
|
||||
async attachEventListeners(reinject = false) {
|
||||
if (!reinject) {
|
||||
await this.pupPage.exposeFunction('onAddMessageEvent', msg => {
|
||||
if (msg.type === 'gp2') {
|
||||
const notification = new GroupNotification(this, msg);
|
||||
if (['add', 'invite', 'linked_group_join'].includes(msg.subtype)) {
|
||||
|
|
@ -440,7 +440,7 @@ class Client extends EventEmitter {
|
|||
|
||||
let last_message;
|
||||
|
||||
await page.exposeFunction('onChangeMessageTypeEvent', (msg) => {
|
||||
await this.pupPage.exposeFunction('onChangeMessageTypeEvent', (msg) => {
|
||||
|
||||
if (msg.type === 'revoked') {
|
||||
const message = new Message(this, msg);
|
||||
|
|
@ -461,7 +461,7 @@ class Client extends EventEmitter {
|
|||
|
||||
});
|
||||
|
||||
await page.exposeFunction('onChangeMessageEvent', (msg) => {
|
||||
await this.pupPage.exposeFunction('onChangeMessageEvent', (msg) => {
|
||||
|
||||
if (msg.type !== 'revoked') {
|
||||
last_message = msg;
|
||||
|
|
@ -499,7 +499,7 @@ class Client extends EventEmitter {
|
|||
}
|
||||
});
|
||||
|
||||
await page.exposeFunction('onRemoveMessageEvent', (msg) => {
|
||||
await this.pupPage.exposeFunction('onRemoveMessageEvent', (msg) => {
|
||||
|
||||
if (!msg.isNewMsg) return;
|
||||
|
||||
|
|
@ -514,7 +514,7 @@ class Client extends EventEmitter {
|
|||
|
||||
});
|
||||
|
||||
await page.exposeFunction('onMessageAckEvent', (msg, ack) => {
|
||||
await this.pupPage.exposeFunction('onMessageAckEvent', (msg, ack) => {
|
||||
|
||||
const message = new Message(this, msg);
|
||||
|
||||
|
|
@ -528,7 +528,7 @@ class Client extends EventEmitter {
|
|||
|
||||
});
|
||||
|
||||
await page.exposeFunction('onChatUnreadCountEvent', async (data) =>{
|
||||
await this.pupPage.exposeFunction('onChatUnreadCountEvent', async (data) =>{
|
||||
const chat = await this.getChatById(data.id);
|
||||
|
||||
/**
|
||||
|
|
@ -537,7 +537,7 @@ class Client extends EventEmitter {
|
|||
this.emit(Events.UNREAD_COUNT, chat);
|
||||
});
|
||||
|
||||
await page.exposeFunction('onMessageMediaUploadedEvent', (msg) => {
|
||||
await this.pupPage.exposeFunction('onMessageMediaUploadedEvent', (msg) => {
|
||||
|
||||
const message = new Message(this, msg);
|
||||
|
||||
|
|
@ -549,8 +549,7 @@ class Client extends EventEmitter {
|
|||
this.emit(Events.MEDIA_UPLOADED, message);
|
||||
});
|
||||
|
||||
await page.exposeFunction('onAppStateChangedEvent', async (state) => {
|
||||
|
||||
await this.pupPage.exposeFunction('onAppStateChangedEvent', async (state) => {
|
||||
/**
|
||||
* Emitted when the connection state changes
|
||||
* @event Client#change_state
|
||||
|
|
@ -574,7 +573,7 @@ class Client extends EventEmitter {
|
|||
/**
|
||||
* Emitted when the client has been disconnected
|
||||
* @event Client#disconnected
|
||||
* @param {WAState|"NAVIGATION"} reason reason that caused the disconnect
|
||||
* @param {WAState|"LOGOUT"} reason reason that caused the disconnect
|
||||
*/
|
||||
await this.authStrategy.disconnect();
|
||||
this.emit(Events.DISCONNECTED, state);
|
||||
|
|
@ -582,7 +581,7 @@ class Client extends EventEmitter {
|
|||
}
|
||||
});
|
||||
|
||||
await page.exposeFunction('onBatteryStateChangedEvent', (state) => {
|
||||
await this.pupPage.exposeFunction('onBatteryStateChangedEvent', (state) => {
|
||||
const { battery, plugged } = state;
|
||||
|
||||
if (battery === undefined) return;
|
||||
|
|
@ -598,7 +597,7 @@ class Client extends EventEmitter {
|
|||
this.emit(Events.BATTERY_CHANGED, { battery, plugged });
|
||||
});
|
||||
|
||||
await page.exposeFunction('onIncomingCall', (call) => {
|
||||
await this.pupPage.exposeFunction('onIncomingCall', (call) => {
|
||||
/**
|
||||
* Emitted when a call is received
|
||||
* @event Client#incoming_call
|
||||
|
|
@ -616,7 +615,7 @@ class Client extends EventEmitter {
|
|||
this.emit(Events.INCOMING_CALL, cll);
|
||||
});
|
||||
|
||||
await page.exposeFunction('onReaction', (reactions) => {
|
||||
await this.pupPage.exposeFunction('onReaction', (reactions) => {
|
||||
for (const reaction of reactions) {
|
||||
/**
|
||||
* Emitted when a reaction is sent, received, updated or removed
|
||||
|
|
@ -637,7 +636,7 @@ class Client extends EventEmitter {
|
|||
}
|
||||
});
|
||||
|
||||
await page.exposeFunction('onRemoveChatEvent', async (chat) => {
|
||||
await this.pupPage.exposeFunction('onRemoveChatEvent', async (chat) => {
|
||||
const _chat = await this.getChatById(chat.id);
|
||||
|
||||
/**
|
||||
|
|
@ -648,7 +647,7 @@ class Client extends EventEmitter {
|
|||
this.emit(Events.CHAT_REMOVED, _chat);
|
||||
});
|
||||
|
||||
await page.exposeFunction('onArchiveChatEvent', async (chat, currState, prevState) => {
|
||||
await this.pupPage.exposeFunction('onArchiveChatEvent', async (chat, currState, prevState) => {
|
||||
const _chat = await this.getChatById(chat.id);
|
||||
|
||||
/**
|
||||
|
|
@ -661,7 +660,7 @@ class Client extends EventEmitter {
|
|||
this.emit(Events.CHAT_ARCHIVED, _chat, currState, prevState);
|
||||
});
|
||||
|
||||
await page.exposeFunction('onEditMessageEvent', (msg, newBody, prevBody) => {
|
||||
await this.pupPage.exposeFunction('onEditMessageEvent', (msg, newBody, prevBody) => {
|
||||
|
||||
if(msg.type === 'revoked'){
|
||||
return;
|
||||
|
|
@ -676,7 +675,7 @@ class Client extends EventEmitter {
|
|||
this.emit(Events.MESSAGE_EDIT, new Message(this, msg), newBody, prevBody);
|
||||
});
|
||||
|
||||
await page.exposeFunction('onAddMessageCiphertextEvent', msg => {
|
||||
await this.pupPage.exposeFunction('onAddMessageCiphertextEvent', msg => {
|
||||
|
||||
/**
|
||||
* Emitted when messages are edited
|
||||
|
|
@ -685,8 +684,9 @@ class Client extends EventEmitter {
|
|||
*/
|
||||
this.emit(Events.MESSAGE_CIPHERTEXT, new Message(this, msg));
|
||||
});
|
||||
}
|
||||
|
||||
await page.exposeFunction('onPollVoteEvent', (vote) => {
|
||||
await this.pupPage.exposeFunction('onPollVoteEvent', (vote) => {
|
||||
const _vote = new PollVote(this, vote);
|
||||
/**
|
||||
* Emitted when some poll option is selected or deselected,
|
||||
|
|
@ -696,7 +696,7 @@ class Client extends EventEmitter {
|
|||
this.emit(Events.VOTE_UPDATE, _vote);
|
||||
});
|
||||
|
||||
await page.evaluate(() => {
|
||||
await this.pupPage.evaluate(() => {
|
||||
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); });
|
||||
|
|
@ -725,7 +725,23 @@ class Client extends EventEmitter {
|
|||
pollVoteModel && window.onPollVoteEvent(pollVoteModel);
|
||||
});
|
||||
|
||||
{
|
||||
if (window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.1014111620')) {
|
||||
const module = window.Store.AddonReactionTable;
|
||||
const ogMethod = module.bulkUpsert;
|
||||
module.bulkUpsert = ((...args) => {
|
||||
window.onReaction(args[0].map(reaction => {
|
||||
const msgKey = reaction.id;
|
||||
const parentMsgKey = reaction.reactionParentKey;
|
||||
const timestamp = reaction.reactionTimestamp / 1000;
|
||||
const sender = reaction.author ?? reaction.from;
|
||||
const senderUserJid = sender._serialized;
|
||||
|
||||
return {...reaction, msgKey, parentMsgKey, senderUserJid, timestamp };
|
||||
}));
|
||||
|
||||
return ogMethod(...args);
|
||||
}).bind(module);
|
||||
} else {
|
||||
const module = window.Store.createOrUpdateReactionsModule;
|
||||
const ogMethod = module.createOrUpdateReactions;
|
||||
module.createOrUpdateReactions = ((...args) => {
|
||||
|
|
@ -741,23 +757,6 @@ class Client extends EventEmitter {
|
|||
}).bind(module);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Emitted when the client has initialized and is ready to receive messages.
|
||||
* @event Client#ready
|
||||
*/
|
||||
this.emit(Events.READY);
|
||||
this.authStrategy.afterAuthReady();
|
||||
|
||||
// Disconnect when navigating away when in PAIRING state (detect logout)
|
||||
this.pupPage.on('framenavigated', async () => {
|
||||
const appState = await this.getState();
|
||||
if(!appState || appState === WAState.PAIRING) {
|
||||
await this.authStrategy.disconnect();
|
||||
this.emit(Events.DISCONNECTED, 'NAVIGATION');
|
||||
await this.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async initWebVersionCache() {
|
||||
|
|
@ -783,7 +782,8 @@ class Client extends EventEmitter {
|
|||
} else {
|
||||
this.pupPage.on('response', async (res) => {
|
||||
if(res.ok() && res.url() === WhatsWebURL) {
|
||||
await webCache.persist(await res.text());
|
||||
const indexHtml = await res.text();
|
||||
this.currentIndexHtml = indexHtml;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -861,6 +861,7 @@ class Client extends EventEmitter {
|
|||
* @property {GroupMention[]} [groupMentions] - An array of object that handle group mentions
|
||||
* @property {string[]} [mentions] - User IDs to mention in the message
|
||||
* @property {boolean} [sendSeen=true] - Mark the conversation as seen after sending the message
|
||||
* @property {string} [invokedBotWid=undefined] - Bot Wid when doing a bot mention like @Meta AI
|
||||
* @property {string} [stickerAuthor=undefined] - Sets the author of the sticker, (if sendMediaAsSticker is true).
|
||||
* @property {string} [stickerName=undefined] - Sets the name of the sticker, (if sendMediaAsSticker is true).
|
||||
* @property {string[]} [stickerCategories=undefined] - Sets the categories of the sticker, (if sendMediaAsSticker is true). Provide emoji char array, can be null.
|
||||
|
|
@ -894,9 +895,10 @@ class Client extends EventEmitter {
|
|||
sendMediaAsDocument: options.sendMediaAsDocument,
|
||||
caption: options.caption,
|
||||
quotedMessageId: options.quotedMessageId,
|
||||
parseVCards: options.parseVCards === false ? false : true,
|
||||
parseVCards: options.parseVCards !== false,
|
||||
mentionedJidList: options.mentions || [],
|
||||
groupMentions: options.groupMentions,
|
||||
invokedBotWid: options.invokedBotWid,
|
||||
extraOptions: options.extra
|
||||
};
|
||||
|
||||
|
|
@ -1102,14 +1104,8 @@ class Client extends EventEmitter {
|
|||
async setDisplayName(displayName) {
|
||||
const couldSet = await this.pupPage.evaluate(async displayName => {
|
||||
if(!window.Store.Conn.canSetMyPushname()) return false;
|
||||
|
||||
if(window.Store.MDBackend) {
|
||||
await window.Store.Settings.setPushname(displayName);
|
||||
return true;
|
||||
} else {
|
||||
const res = await window.Store.Wap.setPushname(displayName);
|
||||
return !res.status || res.status === 200;
|
||||
}
|
||||
}, displayName);
|
||||
|
||||
return couldSet;
|
||||
|
|
@ -1250,7 +1246,9 @@ class Client extends EventEmitter {
|
|||
const profilePic = await this.pupPage.evaluate(async contactId => {
|
||||
try {
|
||||
const chatWid = window.Store.WidFactory.createWid(contactId);
|
||||
return await window.Store.ProfilePic.profilePicFind(chatWid);
|
||||
return window.compareWwebVersions(window.Debug.VERSION, '<', '2.3000.0')
|
||||
? await window.Store.ProfilePic.profilePicFind(chatWid)
|
||||
: await window.Store.ProfilePic.requestProfilePicFromServer(chatWid);
|
||||
} catch (err) {
|
||||
if(err.name === 'ServerStatusCodeError') return undefined;
|
||||
throw err;
|
||||
|
|
@ -1416,10 +1414,18 @@ class Client extends EventEmitter {
|
|||
|
||||
try {
|
||||
createGroupResult = await window.Store.GroupUtils.createGroup(
|
||||
title,
|
||||
participantWids,
|
||||
messageTimer,
|
||||
parentGroupWid
|
||||
{
|
||||
'memberAddMode': options.memberAddMode === undefined ? true : options.memberAddMode,
|
||||
'membershipApprovalMode': options.membershipApprovalMode === undefined ? false : options.membershipApprovalMode,
|
||||
'announce': options.announce === undefined ? true : options.announce,
|
||||
'ephemeralDuration': messageTimer,
|
||||
'full': undefined,
|
||||
'parentGroupId': parentGroupWid,
|
||||
'restrict': options.restrict === undefined ? true : options.restrict,
|
||||
'thumb': undefined,
|
||||
'title': title,
|
||||
},
|
||||
participantWids
|
||||
);
|
||||
} catch (err) {
|
||||
return 'CreateGroupError: An unknown error occupied while creating a group';
|
||||
|
|
@ -1431,7 +1437,7 @@ class Client extends EventEmitter {
|
|||
const statusCode = participant.error ?? 200;
|
||||
|
||||
if (autoSendInviteV4 && statusCode === 403) {
|
||||
window.Store.ContactCollection.gadd(participant.wid, { silent: true });
|
||||
window.Store.Contact.gadd(participant.wid, { silent: true });
|
||||
const addParticipantResult = await window.Store.GroupInviteV4.sendGroupInviteMessage(
|
||||
await window.Store.Chat.find(participant.wid),
|
||||
createGroupResult.wid._serialized,
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const BaseAuthStrategy = require('./BaseAuthStrategy');
|
||||
|
||||
/**
|
||||
* Legacy session auth strategy
|
||||
* Not compatible with multi-device accounts.
|
||||
* @param {object} options - options
|
||||
* @param {string} options.restartOnAuthFail - Restart client with a new session (i.e. use null 'session' var) if authentication fails
|
||||
* @param {object} options.session - Whatsapp session to restore. If not set, will start a new session
|
||||
* @param {string} options.session.WABrowserId
|
||||
* @param {string} options.session.WASecretBundle
|
||||
* @param {string} options.session.WAToken1
|
||||
* @param {string} options.session.WAToken2
|
||||
*/
|
||||
class LegacySessionAuth extends BaseAuthStrategy {
|
||||
constructor({ session, restartOnAuthFail }={}) {
|
||||
super();
|
||||
this.session = session;
|
||||
this.restartOnAuthFail = restartOnAuthFail;
|
||||
}
|
||||
|
||||
async afterBrowserInitialized() {
|
||||
if(this.session) {
|
||||
await this.client.pupPage.evaluateOnNewDocument(session => {
|
||||
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);
|
||||
}
|
||||
|
||||
localStorage.setItem('remember-me', 'true');
|
||||
}, this.session);
|
||||
}
|
||||
}
|
||||
|
||||
async onAuthenticationNeeded() {
|
||||
if(this.session) {
|
||||
this.session = null;
|
||||
return {
|
||||
failed: true,
|
||||
restart: this.restartOnAuthFail,
|
||||
failureEventPayload: 'Unable to log in. Are the session details valid?'
|
||||
};
|
||||
}
|
||||
|
||||
return { failed: false };
|
||||
}
|
||||
|
||||
async getAuthEventPayload() {
|
||||
const isMD = await this.client.pupPage.evaluate(() => {
|
||||
return window.Store.MDBackend;
|
||||
});
|
||||
|
||||
if(isMD) throw new Error('Authenticating via JSON session is not supported for MultiDevice-enabled WhatsApp accounts.');
|
||||
|
||||
const localStorage = JSON.parse(await this.client.pupPage.evaluate(() => {
|
||||
return JSON.stringify(window.localStorage);
|
||||
}));
|
||||
|
||||
return {
|
||||
WABrowserId: localStorage.WABrowserId,
|
||||
WASecretBundle: localStorage.WASecretBundle,
|
||||
WAToken1: localStorage.WAToken1,
|
||||
WAToken2: localStorage.WAToken2
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LegacySessionAuth;
|
||||
|
|
@ -44,7 +44,10 @@ class LocalAuth extends BaseAuthStrategy {
|
|||
|
||||
async logout() {
|
||||
if (this.userDataDir) {
|
||||
return (fs.rmSync ? fs.rmSync : fs.rmdirSync).call(this, this.userDataDir, { recursive: true, force: true });
|
||||
await fs.promises.rm(this.userDataDir, { recursive: true, force: true })
|
||||
.catch((e) => {
|
||||
throw new Error(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ class GroupChat extends Chat {
|
|||
419: 'The participant can\'t be added because the group is full'
|
||||
};
|
||||
|
||||
await window.Store.GroupMetadata.queryAndUpdate(groupWid);
|
||||
await window.Store.GroupQueryAndUpdate(groupWid);
|
||||
const groupMetadata = group.groupMetadata;
|
||||
const groupParticipants = groupMetadata?.participants;
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ class GroupChat extends Chat {
|
|||
|
||||
if (autoSendInviteV4 && rpcResultCode === 403) {
|
||||
let userChat, isInviteV4Sent = false;
|
||||
window.Store.ContactCollection.gadd(pWid, { silent: true });
|
||||
window.Store.Contact.gadd(pWid, { silent: true });
|
||||
|
||||
if (rpcResult.name === 'ParticipantRequestCodeCanBeSent' &&
|
||||
(userChat = await window.Store.Chat.find(pWid))) {
|
||||
|
|
@ -380,10 +380,18 @@ class GroupChat extends Chat {
|
|||
async getInviteCode() {
|
||||
const codeRes = await this.client.pupPage.evaluate(async chatId => {
|
||||
const chatWid = window.Store.WidFactory.createWid(chatId);
|
||||
return window.Store.GroupInvite.queryGroupInviteCode(chatWid);
|
||||
try {
|
||||
return window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.0')
|
||||
? await window.Store.GroupInvite.queryGroupInviteCode(chatWid, true)
|
||||
: await window.Store.GroupInvite.queryGroupInviteCode(chatWid);
|
||||
}
|
||||
catch (err) {
|
||||
if(err.name === 'ServerStatusCodeError') return undefined;
|
||||
throw err;
|
||||
}
|
||||
}, this.id._serialized);
|
||||
|
||||
return codeRes.code;
|
||||
return codeRes?.code;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -434,10 +434,7 @@ class Message extends Base {
|
|||
const chatId = typeof chat === 'string' ? chat : chat.id._serialized;
|
||||
|
||||
await this.client.pupPage.evaluate(async (msgId, chatId) => {
|
||||
let msg = window.Store.Msg.get(msgId);
|
||||
let chat = window.Store.Chat.get(chatId);
|
||||
|
||||
return await chat.forwardMessages([msg]);
|
||||
return window.WWebJS.forwardMessage(chatId, msgId);
|
||||
}, this.id._serialized, chatId);
|
||||
}
|
||||
|
||||
|
|
@ -508,8 +505,12 @@ class Message extends Base {
|
|||
|
||||
const canRevoke = window.Store.MsgActionChecks.canSenderRevokeMsg(msg) || window.Store.MsgActionChecks.canAdminRevokeMsg(msg);
|
||||
if (everyone && canRevoke) {
|
||||
if (window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.0')) {
|
||||
return window.Store.Cmd.sendRevokeMsgs(chat, { list: [msg], type: 'message' }, { clearMedia: true });
|
||||
} else {
|
||||
return window.Store.Cmd.sendRevokeMsgs(chat, [msg], { clearMedia: true, type: msg.id.fromMe ? 'Sender' : 'Admin' });
|
||||
}
|
||||
}
|
||||
|
||||
return window.Store.Cmd.sendDeleteMsgs(chat, [msg], true);
|
||||
}, this.id._serialized, everyone);
|
||||
|
|
|
|||
17
src/util/Injected/AuthStore/AuthStore.js
Normal file
17
src/util/Injected/AuthStore/AuthStore.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
exports.ExposeAuthStore = () => {
|
||||
window.AuthStore = {};
|
||||
window.AuthStore.AppState = window.require('WAWebSocketModel').Socket;
|
||||
window.AuthStore.Cmd = window.require('WAWebCmd').Cmd;
|
||||
window.AuthStore.Conn = window.require('WAWebConnModel').Conn;
|
||||
window.AuthStore.OfflineMessageHandler = window.require('WAWebOfflineHandler').OfflineMessageHandler;
|
||||
window.AuthStore.PairingCodeLinkUtils = window.require('WAWebAltDeviceLinkingApi');
|
||||
window.AuthStore.Base64Tools = window.require('WABase64');
|
||||
window.AuthStore.RegistrationUtils = {
|
||||
...window.require('WAWebCompanionRegClientUtils'),
|
||||
...window.require('WAWebAdvSignatureApi'),
|
||||
...window.require('WAWebUserPrefsInfoStore'),
|
||||
...window.require('WAWebSignalStoreApi'),
|
||||
};
|
||||
};
|
||||
22
src/util/Injected/AuthStore/LegacyAuthStore.js
Normal file
22
src/util/Injected/AuthStore/LegacyAuthStore.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
'use strict';
|
||||
|
||||
//TODO: To be removed by version 2.3000.x hard release
|
||||
|
||||
exports.ExposeLegacyAuthStore = (moduleRaidStr) => {
|
||||
eval('var moduleRaid = ' + moduleRaidStr);
|
||||
// eslint-disable-next-line no-undef
|
||||
window.mR = moduleRaid();
|
||||
window.AuthStore = {};
|
||||
window.AuthStore.AppState = window.mR.findModule('Socket')[0].Socket;
|
||||
window.AuthStore.Cmd = window.mR.findModule('Cmd')[0].Cmd;
|
||||
window.AuthStore.Conn = window.mR.findModule('Conn')[0].Conn;
|
||||
window.AuthStore.OfflineMessageHandler = window.mR.findModule('OfflineMessageHandler')[0].OfflineMessageHandler;
|
||||
window.AuthStore.PairingCodeLinkUtils = window.mR.findModule('initializeAltDeviceLinking')[0];
|
||||
window.AuthStore.Base64Tools = window.mR.findModule('encodeB64')[0];
|
||||
window.AuthStore.RegistrationUtils = {
|
||||
...window.mR.findModule('getCompanionWebClientFromBrowser')[0],
|
||||
...window.mR.findModule('verifyKeyIndexListAccountSignature')[0],
|
||||
...window.mR.findModule('waNoiseInfo')[0],
|
||||
...window.mR.findModule('waSignalStore')[0],
|
||||
};
|
||||
};
|
||||
146
src/util/Injected/LegacyStore.js
Normal file
146
src/util/Injected/LegacyStore.js
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
'use strict';
|
||||
|
||||
//TODO: To be removed by version 2.3000.x hard release
|
||||
|
||||
// Exposes the internal Store to the WhatsApp Web client
|
||||
exports.ExposeLegacyStore = () => {
|
||||
window.Store = Object.assign({}, window.mR.findModule(m => m.default && m.default.Chat)[0].default);
|
||||
window.Store.AppState = window.mR.findModule('Socket')[0].Socket;
|
||||
window.Store.Conn = window.mR.findModule('Conn')[0].Conn;
|
||||
window.Store.BlockContact = window.mR.findModule('blockContact')[0];
|
||||
window.Store.Call = window.mR.findModule((module) => module.default && module.default.Call)[0].default.Call;
|
||||
window.Store.Cmd = window.mR.findModule('Cmd')[0].Cmd;
|
||||
window.Store.CryptoLib = window.mR.findModule('decryptE2EMedia')[0];
|
||||
window.Store.DownloadManager = window.mR.findModule('downloadManager')[0].downloadManager;
|
||||
window.Store.GroupMetadata = window.mR.findModule('GroupMetadata')[0].default.GroupMetadata;
|
||||
window.Store.GroupQueryAndUpdate = window.mR.findModule('queryAndUpdateGroupMetadataById')[0].queryAndUpdateGroupMetadataById;
|
||||
window.Store.Label = window.mR.findModule('LabelCollection')[0].LabelCollection;
|
||||
window.Store.MediaPrep = window.mR.findModule('prepRawMedia')[0];
|
||||
window.Store.MediaObject = window.mR.findModule('getOrCreateMediaObject')[0];
|
||||
window.Store.NumberInfo = window.mR.findModule('formattedPhoneNumber')[0];
|
||||
window.Store.MediaTypes = window.mR.findModule('msgToMediaType')[0];
|
||||
window.Store.MediaUpload = window.mR.findModule('uploadMedia')[0];
|
||||
window.Store.MsgKey = window.mR.findModule((module) => module.default && module.default.fromString)[0].default;
|
||||
window.Store.OpaqueData = window.mR.findModule(module => module.default && module.default.createFromData)[0].default;
|
||||
window.Store.QueryProduct = window.mR.findModule('queryProduct')[0];
|
||||
window.Store.QueryOrder = window.mR.findModule('queryOrder')[0];
|
||||
window.Store.SendClear = window.mR.findModule('sendClear')[0];
|
||||
window.Store.SendDelete = window.mR.findModule('sendDelete')[0];
|
||||
window.Store.SendMessage = window.mR.findModule('addAndSendMsgToChat')[0];
|
||||
window.Store.EditMessage = window.mR.findModule('addAndSendMessageEdit')[0];
|
||||
window.Store.SendSeen = window.mR.findModule('sendSeen')[0];
|
||||
window.Store.User = window.mR.findModule('getMaybeMeUser')[0];
|
||||
window.Store.ContactMethods = window.mR.findModule('getUserid')[0];
|
||||
window.Store.UploadUtils = window.mR.findModule((module) => (module.default && module.default.encryptAndUpload) ? module.default : null)[0].default;
|
||||
window.Store.UserConstructor = window.mR.findModule((module) => (module.default && module.default.prototype && module.default.prototype.isServer && module.default.prototype.isUser) ? module.default : null)[0].default;
|
||||
window.Store.Validators = window.mR.findModule('findLinks')[0];
|
||||
window.Store.VCard = window.mR.findModule('vcardFromContactModel')[0];
|
||||
window.Store.WidFactory = window.mR.findModule('createWid')[0];
|
||||
window.Store.ProfilePic = window.mR.findModule('profilePicResync')[0];
|
||||
window.Store.PresenceUtils = window.mR.findModule('sendPresenceAvailable')[0];
|
||||
window.Store.ChatState = window.mR.findModule('sendChatStateComposing')[0];
|
||||
window.Store.findCommonGroups = window.mR.findModule('findCommonGroups')[0].findCommonGroups;
|
||||
window.Store.StatusUtils = window.mR.findModule('setMyStatus')[0];
|
||||
window.Store.ConversationMsgs = window.mR.findModule('loadEarlierMsgs')[0];
|
||||
window.Store.sendReactionToMsg = window.mR.findModule('sendReactionToMsg')[0].sendReactionToMsg;
|
||||
window.Store.createOrUpdateReactionsModule = window.mR.findModule('createOrUpdateReactions')[0];
|
||||
window.Store.EphemeralFields = window.mR.findModule('getEphemeralFields')[0];
|
||||
window.Store.MsgActionChecks = window.mR.findModule('canSenderRevokeMsg')[0];
|
||||
window.Store.QuotedMsg = window.mR.findModule('getQuotedMsgObj')[0];
|
||||
window.Store.LinkPreview = window.mR.findModule('getLinkPreview')[0];
|
||||
window.Store.Socket = window.mR.findModule('deprecatedSendIq')[0];
|
||||
window.Store.SocketWap = window.mR.findModule('wap')[0];
|
||||
window.Store.SearchContext = window.mR.findModule('getSearchContext')[0].getSearchContext;
|
||||
window.Store.DrawerManager = window.mR.findModule('DrawerManager')[0].DrawerManager;
|
||||
window.Store.LidUtils = window.mR.findModule('getCurrentLid')[0];
|
||||
window.Store.WidToJid = window.mR.findModule('widToUserJid')[0];
|
||||
window.Store.JidToWid = window.mR.findModule('userJidToUserWid')[0];
|
||||
window.Store.getMsgInfo = (window.mR.findModule('sendQueryMsgInfo')[0] || {}).sendQueryMsgInfo || window.mR.findModule('queryMsgInfo')[0].queryMsgInfo;
|
||||
window.Store.pinUnpinMsg = window.mR.findModule('sendPinInChatMsg')[0].sendPinInChatMsg;
|
||||
|
||||
/* eslint-disable no-undef, no-cond-assign */
|
||||
window.Store.QueryExist = ((m = window.mR.findModule('queryExists')[0]) ? m.queryExists : window.mR.findModule('queryExist')[0].queryWidExists);
|
||||
window.Store.ReplyUtils = (m = window.mR.findModule('canReplyMsg')).length > 0 && m[0];
|
||||
/* eslint-enable no-undef, no-cond-assign */
|
||||
|
||||
window.Store.Settings = {
|
||||
...window.mR.findModule('ChatlistPanelState')[0],
|
||||
setPushname: window.mR.findModule((m) => m.setPushname && !m.ChatlistPanelState)[0].setPushname
|
||||
};
|
||||
window.Store.StickerTools = {
|
||||
...window.mR.findModule('toWebpSticker')[0],
|
||||
...window.mR.findModule('addWebpMetadata')[0]
|
||||
};
|
||||
window.Store.GroupUtils = {
|
||||
...window.mR.findModule('createGroup')[0],
|
||||
...window.mR.findModule('setGroupDescription')[0],
|
||||
...window.mR.findModule('sendExitGroup')[0],
|
||||
...window.mR.findModule('sendSetPicture')[0]
|
||||
};
|
||||
window.Store.GroupParticipants = {
|
||||
...window.mR.findModule('promoteParticipants')[0],
|
||||
...window.mR.findModule('sendAddParticipantsRPC')[0]
|
||||
};
|
||||
window.Store.GroupInvite = {
|
||||
...window.mR.findModule('resetGroupInviteCode')[0],
|
||||
...window.mR.findModule('queryGroupInvite')[0]
|
||||
};
|
||||
window.Store.GroupInviteV4 = {
|
||||
...window.mR.findModule('queryGroupInviteV4')[0],
|
||||
...window.mR.findModule('sendGroupInviteMessage')[0]
|
||||
};
|
||||
window.Store.MembershipRequestUtils = {
|
||||
...window.mR.findModule('getMembershipApprovalRequests')[0],
|
||||
...window.mR.findModule('sendMembershipRequestsActionRPC')[0]
|
||||
};
|
||||
|
||||
if (!window.Store.Chat._find) {
|
||||
window.Store.Chat._find = e => {
|
||||
const target = window.Store.Chat.get(e);
|
||||
return target ? Promise.resolve(target) : Promise.resolve({
|
||||
id: e
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
if ((m = window.mR.findModule('ChatCollection')[0]) && m.ChatCollection && typeof m.ChatCollection.findImpl === 'undefined' && typeof m.ChatCollection._find !== 'undefined') m.ChatCollection.findImpl = m.ChatCollection._find;
|
||||
|
||||
const _isMDBackend = window.mR.findModule('isMDBackend');
|
||||
if(_isMDBackend && _isMDBackend[0] && _isMDBackend[0].isMDBackend) {
|
||||
window.Store.MDBackend = _isMDBackend[0].isMDBackend();
|
||||
} else {
|
||||
window.Store.MDBackend = true;
|
||||
}
|
||||
|
||||
const _features = window.mR.findModule('FEATURE_CHANGE_EVENT')[0];
|
||||
if(_features) {
|
||||
window.Store.Features = _features.LegacyPhoneFeatures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Target options object description
|
||||
* @typedef {Object} TargetOptions
|
||||
* @property {string|number} module The name or a key of the target module to search
|
||||
* @property {number} index The index value of the target module
|
||||
* @property {string} function The function name to get from a module
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function to modify functions
|
||||
* @param {TargetOptions} target Options specifying the target function to search for modifying
|
||||
* @param {Function} callback Modified function
|
||||
*/
|
||||
window.injectToFunction = (target, callback) => {
|
||||
const module = typeof target.module === 'string'
|
||||
? window.mR.findModule(target.module)
|
||||
: window.mR.modules[target.module];
|
||||
const originalFunction = module[target.index][target.function];
|
||||
const modifiedFunction = (...args) => callback(originalFunction, ...args);
|
||||
module[target.index][target.function] = modifiedFunction;
|
||||
};
|
||||
|
||||
window.injectToFunction({ module: 'mediaTypeFromProtobuf', index: 0, function: 'mediaTypeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage ? null : func(...args); });
|
||||
|
||||
window.injectToFunction({ module: 'typeAttributeFromProtobuf', index: 0, function: 'typeAttributeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage || proto.groupInviteMessage ? 'text' : func(...args); });
|
||||
};
|
||||
166
src/util/Injected/Store.js
Normal file
166
src/util/Injected/Store.js
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
'use strict';
|
||||
|
||||
exports.ExposeStore = () => {
|
||||
/**
|
||||
* Helper function that compares between two WWeb versions. Its purpose is to help the developer to choose the correct code implementation depending on the comparison value and the WWeb version.
|
||||
* @param {string} lOperand The left operand for the WWeb version string to compare with
|
||||
* @param {string} operator The comparison operator
|
||||
* @param {string} rOperand The right operand for the WWeb version string to compare with
|
||||
* @returns {boolean} Boolean value that indicates the result of the comparison
|
||||
*/
|
||||
window.compareWwebVersions = (lOperand, operator, rOperand) => {
|
||||
if (!['>', '>=', '<', '<=', '='].includes(operator)) {
|
||||
throw new class _ extends Error {
|
||||
constructor(m) { super(m); this.name = 'CompareWwebVersionsError'; }
|
||||
}('Invalid comparison operator is provided');
|
||||
|
||||
}
|
||||
if (typeof lOperand !== 'string' || typeof rOperand !== 'string') {
|
||||
throw new class _ extends Error {
|
||||
constructor(m) { super(m); this.name = 'CompareWwebVersionsError'; }
|
||||
}('A non-string WWeb version type is provided');
|
||||
}
|
||||
|
||||
lOperand = lOperand.replace(/-beta$/, '');
|
||||
rOperand = rOperand.replace(/-beta$/, '');
|
||||
|
||||
while (lOperand.length !== rOperand.length) {
|
||||
lOperand.length > rOperand.length
|
||||
? rOperand = rOperand.concat('0')
|
||||
: lOperand = lOperand.concat('0');
|
||||
}
|
||||
|
||||
lOperand = Number(lOperand.replace(/\./g, ''));
|
||||
rOperand = Number(rOperand.replace(/\./g, ''));
|
||||
|
||||
return (
|
||||
operator === '>' ? lOperand > rOperand :
|
||||
operator === '>=' ? lOperand >= rOperand :
|
||||
operator === '<' ? lOperand < rOperand :
|
||||
operator === '<=' ? lOperand <= rOperand :
|
||||
operator === '=' ? lOperand === rOperand :
|
||||
false
|
||||
);
|
||||
};
|
||||
|
||||
window.Store = Object.assign({}, window.require('WAWebCollections'));
|
||||
window.Store.AppState = window.require('WAWebSocketModel').Socket;
|
||||
window.Store.BlockContact = window.require('WAWebBlockContactAction');
|
||||
window.Store.Conn = window.require('WAWebConnModel').Conn;
|
||||
window.Store.Cmd = window.require('WAWebCmd').Cmd;
|
||||
window.Store.DownloadManager = window.require('WAWebDownloadManager').downloadManager;
|
||||
window.Store.GroupQueryAndUpdate = window.require('WAWebGroupQueryJob').queryAndUpdateGroupMetadataById;
|
||||
window.Store.MediaPrep = window.require('WAWebPrepRawMedia');
|
||||
window.Store.MediaObject = window.require('WAWebMediaStorage');
|
||||
window.Store.MediaTypes = window.require('WAWebMmsMediaTypes');
|
||||
window.Store.MediaUpload = window.require('WAWebMediaMmsV4Upload');
|
||||
window.Store.MsgKey = window.require('WAWebMsgKey');
|
||||
window.Store.NumberInfo = window.require('WAPhoneUtils');
|
||||
window.Store.OpaqueData = window.require('WAWebMediaOpaqueData');
|
||||
window.Store.QueryProduct = window.require('WAWebBizProductCatalogBridge');
|
||||
window.Store.QueryOrder = window.require('WAWebBizOrderBridge');
|
||||
window.Store.SendClear = window.require('WAWebChatClearBridge');
|
||||
window.Store.SendDelete = window.require('WAWebDeleteChatAction');
|
||||
window.Store.SendMessage = window.require('WAWebSendMsgChatAction');
|
||||
window.Store.EditMessage = window.require('WAWebSendMessageEditAction');
|
||||
window.Store.SendSeen = window.require('WAWebUpdateUnreadChatAction');
|
||||
window.Store.User = window.require('WAWebUserPrefsMeUser');
|
||||
window.Store.ContactMethods = window.require('WAWebContactGetters');
|
||||
window.Store.UploadUtils = window.require('WAWebUploadManager');
|
||||
window.Store.UserConstructor = window.require('WAWebWid');
|
||||
window.Store.Validators = window.require('WALinkify');
|
||||
window.Store.VCard = window.require('WAWebFrontendVcardUtils');
|
||||
window.Store.WidFactory = window.require('WAWebWidFactory');
|
||||
window.Store.ProfilePic = window.require('WAWebContactProfilePicThumbBridge');
|
||||
window.Store.PresenceUtils = window.require('WAWebPresenceChatAction');
|
||||
window.Store.ChatState = window.require('WAWebChatStateBridge');
|
||||
window.Store.findCommonGroups = window.require('WAWebFindCommonGroupsContactAction').findCommonGroups;
|
||||
window.Store.StatusUtils = window.require('WAWebContactStatusBridge');
|
||||
window.Store.ConversationMsgs = window.require('WAWebChatLoadMessages');
|
||||
window.Store.sendReactionToMsg = window.require('WAWebSendReactionMsgAction').sendReactionToMsg;
|
||||
window.Store.createOrUpdateReactionsModule = window.require('WAWebDBCreateOrUpdateReactions');
|
||||
window.Store.EphemeralFields = window.require('WAWebGetEphemeralFieldsMsgActionsUtils');
|
||||
window.Store.MsgActionChecks = window.require('WAWebMsgActionCapability');
|
||||
window.Store.QuotedMsg = window.require('WAWebQuotedMsgModelUtils');
|
||||
window.Store.LinkPreview = window.require('WAWebLinkPreviewChatAction');
|
||||
window.Store.Socket = window.require('WADeprecatedSendIq');
|
||||
window.Store.SocketWap = window.require('WAWap');
|
||||
window.Store.SearchContext = window.require('WAWebChatMessageSearch').getSearchContext;
|
||||
window.Store.DrawerManager = window.require('WAWebDrawerManager').DrawerManager;
|
||||
window.Store.LidUtils = window.require('WAWebApiContact');
|
||||
window.Store.WidToJid = window.require('WAWebWidToJid');
|
||||
window.Store.JidToWid = window.require('WAWebJidToWid');
|
||||
window.Store.getMsgInfo = window.require('WAWebApiMessageInfoStore').queryMsgInfo;
|
||||
window.Store.pinUnpinMsg = window.require('WAWebSendPinMessageAction').sendPinInChatMsg;
|
||||
window.Store.QueryExist = window.require('WAWebQueryExistsJob').queryWidExists;
|
||||
window.Store.ReplyUtils = window.require('WAWebMsgReply');
|
||||
window.Store.Settings = window.require('WAWebUserPrefsGeneral');
|
||||
window.Store.BotSecret = window.require('WAWebBotMessageSecret');
|
||||
window.Store.BotProfiles = window.require('WAWebBotProfileCollection');
|
||||
if (window.compareWwebVersions(window.Debug.VERSION, '>=', '2.3000.1014111620'))
|
||||
window.Store.AddonReactionTable = window.require('WAWebAddonReactionTableMode').reactionTableMode;
|
||||
|
||||
window.Store.ForwardUtils = {
|
||||
...window.require('WAWebForwardMessagesToChat')
|
||||
};
|
||||
|
||||
window.Store.StickerTools = {
|
||||
...window.require('WAWebImageUtils'),
|
||||
...window.require('WAWebAddWebpMetadata')
|
||||
};
|
||||
window.Store.GroupUtils = {
|
||||
...window.require('WAWebGroupCreateJob'),
|
||||
...window.require('WAWebGroupModifyInfoJob'),
|
||||
...window.require('WAWebExitGroupAction'),
|
||||
...window.require('WAWebContactProfilePicThumbBridge')
|
||||
};
|
||||
window.Store.GroupParticipants = {
|
||||
...window.require('WAWebModifyParticipantsGroupAction'),
|
||||
...window.require('WASmaxGroupsAddParticipantsRPC')
|
||||
};
|
||||
window.Store.GroupInvite = {
|
||||
...window.require('WAWebGroupInviteJob'),
|
||||
...window.require('WAWebGroupQueryJob')
|
||||
};
|
||||
window.Store.GroupInviteV4 = {
|
||||
...window.require('WAWebGroupInviteV4Job'),
|
||||
...window.require('WAWebChatSendMessages')
|
||||
};
|
||||
window.Store.MembershipRequestUtils = {
|
||||
...window.require('WAWebApiMembershipApprovalRequestStore'),
|
||||
...window.require('WASmaxGroupsMembershipRequestsActionRPC')
|
||||
};
|
||||
|
||||
if (!window.Store.Chat._find || !window.Store.Chat.findImpl) {
|
||||
window.Store.Chat._find = e => {
|
||||
const target = window.Store.Chat.get(e);
|
||||
return target ? Promise.resolve(target) : Promise.resolve({
|
||||
id: e
|
||||
});
|
||||
};
|
||||
window.Store.Chat.findImpl = window.Store.Chat._find;
|
||||
}
|
||||
|
||||
/**
|
||||
* Target options object description
|
||||
* @typedef {Object} TargetOptions
|
||||
* @property {string|number} module The target module
|
||||
* @property {string} function The function name to get from a module
|
||||
*/
|
||||
/**
|
||||
* Function to modify functions
|
||||
* @param {TargetOptions} target Options specifying the target function to search for modifying
|
||||
* @param {Function} callback Modified function
|
||||
*/
|
||||
window.injectToFunction = (target, callback) => {
|
||||
const module = window.require(target.module);
|
||||
const originalFunction = module[target.function];
|
||||
const modifiedFunction = (...args) => callback(originalFunction, ...args);
|
||||
module[target.function] = modifiedFunction;
|
||||
};
|
||||
|
||||
window.injectToFunction({ module: 'WAWebBackendJobsCommon', function: 'mediaTypeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage ? null : func(...args); });
|
||||
|
||||
window.injectToFunction({ module: 'WAWebE2EProtoUtils', function: 'typeAttributeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage || proto.groupInviteMessage ? 'text' : func(...args); });
|
||||
|
||||
};
|
||||
|
|
@ -1,156 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
// Exposes the internal Store to the WhatsApp Web client
|
||||
exports.ExposeStore = (moduleRaidStr) => {
|
||||
eval('var moduleRaid = ' + moduleRaidStr);
|
||||
// eslint-disable-next-line no-undef
|
||||
window.mR = moduleRaid();
|
||||
window.Store = Object.assign({}, window.mR.findModule(m => m.default && m.default.Chat)[0].default);
|
||||
window.Store.AppState = window.mR.findModule('Socket')[0].Socket;
|
||||
window.Store.Conn = window.mR.findModule('Conn')[0].Conn;
|
||||
window.Store.BlockContact = window.mR.findModule('blockContact')[0];
|
||||
window.Store.Call = window.mR.findModule((module) => module.default && module.default.Call)[0].default.Call;
|
||||
window.Store.Cmd = window.mR.findModule('Cmd')[0].Cmd;
|
||||
window.Store.CryptoLib = window.mR.findModule('decryptE2EMedia')[0];
|
||||
window.Store.DownloadManager = window.mR.findModule('downloadManager')[0].downloadManager;
|
||||
window.Store.GroupMetadata = window.mR.findModule('GroupMetadata')[0].default.GroupMetadata;
|
||||
window.Store.GroupMetadata.queryAndUpdate = window.mR.findModule('queryAndUpdateGroupMetadataById')[0].queryAndUpdateGroupMetadataById;
|
||||
window.Store.Label = window.mR.findModule('LabelCollection')[0].LabelCollection;
|
||||
window.Store.ContactCollection = window.mR.findModule('ContactCollection')[0].ContactCollection;
|
||||
window.Store.MediaPrep = window.mR.findModule('prepRawMedia')[0];
|
||||
window.Store.MediaObject = window.mR.findModule('getOrCreateMediaObject')[0];
|
||||
window.Store.NumberInfo = window.mR.findModule('formattedPhoneNumber')[0];
|
||||
window.Store.MediaTypes = window.mR.findModule('msgToMediaType')[0];
|
||||
window.Store.MediaUpload = window.mR.findModule('uploadMedia')[0];
|
||||
window.Store.MsgKey = window.mR.findModule((module) => module.default && module.default.fromString)[0].default;
|
||||
window.Store.OpaqueData = window.mR.findModule(module => module.default && module.default.createFromData)[0].default;
|
||||
window.Store.QueryProduct = window.mR.findModule('queryProduct')[0];
|
||||
window.Store.QueryOrder = window.mR.findModule('queryOrder')[0];
|
||||
window.Store.SendClear = window.mR.findModule('sendClear')[0];
|
||||
window.Store.SendDelete = window.mR.findModule('sendDelete')[0];
|
||||
window.Store.SendMessage = window.mR.findModule('addAndSendMsgToChat')[0];
|
||||
window.Store.EditMessage = window.mR.findModule('addAndSendMessageEdit')[0];
|
||||
window.Store.SendSeen = window.mR.findModule('sendSeen')[0];
|
||||
window.Store.User = window.mR.findModule('getMaybeMeUser')[0];
|
||||
window.Store.ContactMethods = window.mR.findModule('getUserid')[0];
|
||||
window.Store.BusinessProfileCollection = window.mR.findModule('BusinessProfileCollection')[0].BusinessProfileCollection;
|
||||
window.Store.UploadUtils = window.mR.findModule((module) => (module.default && module.default.encryptAndUpload) ? module.default : null)[0].default;
|
||||
window.Store.UserConstructor = window.mR.findModule((module) => (module.default && module.default.prototype && module.default.prototype.isServer && module.default.prototype.isUser) ? module.default : null)[0].default;
|
||||
window.Store.Validators = window.mR.findModule('findLinks')[0];
|
||||
window.Store.VCard = window.mR.findModule('vcardFromContactModel')[0];
|
||||
window.Store.WidFactory = window.mR.findModule('createWid')[0];
|
||||
window.Store.ProfilePic = window.mR.findModule('profilePicResync')[0];
|
||||
window.Store.PresenceUtils = window.mR.findModule('sendPresenceAvailable')[0];
|
||||
window.Store.ChatState = window.mR.findModule('sendChatStateComposing')[0];
|
||||
window.Store.findCommonGroups = window.mR.findModule('findCommonGroups')[0].findCommonGroups;
|
||||
window.Store.StatusUtils = window.mR.findModule('setMyStatus')[0];
|
||||
window.Store.ConversationMsgs = window.mR.findModule('loadEarlierMsgs')[0];
|
||||
window.Store.sendReactionToMsg = window.mR.findModule('sendReactionToMsg')[0].sendReactionToMsg;
|
||||
window.Store.createOrUpdateReactionsModule = window.mR.findModule('createOrUpdateReactions')[0];
|
||||
window.Store.EphemeralFields = window.mR.findModule('getEphemeralFields')[0];
|
||||
window.Store.MsgActionChecks = window.mR.findModule('canSenderRevokeMsg')[0];
|
||||
window.Store.QuotedMsg = window.mR.findModule('getQuotedMsgObj')[0];
|
||||
window.Store.LinkPreview = window.mR.findModule('getLinkPreview')[0];
|
||||
window.Store.Socket = window.mR.findModule('deprecatedSendIq')[0];
|
||||
window.Store.SocketWap = window.mR.findModule('wap')[0];
|
||||
window.Store.SearchContext = window.mR.findModule('getSearchContext')[0].getSearchContext;
|
||||
window.Store.DrawerManager = window.mR.findModule('DrawerManager')[0].DrawerManager;
|
||||
window.Store.LidUtils = window.mR.findModule('getCurrentLid')[0];
|
||||
window.Store.WidToJid = window.mR.findModule('widToUserJid')[0];
|
||||
window.Store.JidToWid = window.mR.findModule('userJidToUserWid')[0];
|
||||
window.Store.getMsgInfo = (window.mR.findModule('sendQueryMsgInfo')[0] || {}).sendQueryMsgInfo || window.mR.findModule('queryMsgInfo')[0].queryMsgInfo;
|
||||
window.Store.pinUnpinMsg = window.mR.findModule('sendPinInChatMsg')[0].sendPinInChatMsg;
|
||||
|
||||
/* eslint-disable no-undef, no-cond-assign */
|
||||
window.Store.QueryExist = ((m = window.mR.findModule('queryExists')[0]) ? m.queryExists : window.mR.findModule('queryExist')[0].queryWidExists);
|
||||
window.Store.ReplyUtils = (m = window.mR.findModule('canReplyMsg')).length > 0 && m[0];
|
||||
/* eslint-enable no-undef, no-cond-assign */
|
||||
|
||||
window.Store.Settings = {
|
||||
...window.mR.findModule('ChatlistPanelState')[0],
|
||||
setPushname: window.mR.findModule((m) => m.setPushname && !m.ChatlistPanelState)[0].setPushname
|
||||
};
|
||||
window.Store.StickerTools = {
|
||||
...window.mR.findModule('toWebpSticker')[0],
|
||||
...window.mR.findModule('addWebpMetadata')[0]
|
||||
};
|
||||
window.Store.GroupUtils = {
|
||||
...window.mR.findModule('createGroup')[0],
|
||||
...window.mR.findModule('setGroupDescription')[0],
|
||||
...window.mR.findModule('sendExitGroup')[0],
|
||||
...window.mR.findModule('sendSetPicture')[0]
|
||||
};
|
||||
window.Store.GroupParticipants = {
|
||||
...window.mR.findModule('promoteParticipants')[0],
|
||||
...window.mR.findModule('sendAddParticipantsRPC')[0]
|
||||
};
|
||||
window.Store.GroupInvite = {
|
||||
...window.mR.findModule('resetGroupInviteCode')[0],
|
||||
...window.mR.findModule('queryGroupInvite')[0]
|
||||
};
|
||||
window.Store.GroupInviteV4 = {
|
||||
...window.mR.findModule('queryGroupInviteV4')[0],
|
||||
...window.mR.findModule('sendGroupInviteMessage')[0]
|
||||
};
|
||||
window.Store.MembershipRequestUtils = {
|
||||
...window.mR.findModule('getMembershipApprovalRequests')[0],
|
||||
...window.mR.findModule('sendMembershipRequestsActionRPC')[0]
|
||||
};
|
||||
|
||||
if (!window.Store.Chat._find) {
|
||||
window.Store.Chat._find = e => {
|
||||
const target = window.Store.Chat.get(e);
|
||||
return target ? Promise.resolve(target) : Promise.resolve({
|
||||
id: e
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
if ((m = window.mR.findModule('ChatCollection')[0]) && m.ChatCollection && typeof m.ChatCollection.findImpl === 'undefined' && typeof m.ChatCollection._find !== 'undefined') m.ChatCollection.findImpl = m.ChatCollection._find;
|
||||
|
||||
const _isMDBackend = window.mR.findModule('isMDBackend');
|
||||
if(_isMDBackend && _isMDBackend[0] && _isMDBackend[0].isMDBackend) {
|
||||
window.Store.MDBackend = _isMDBackend[0].isMDBackend();
|
||||
} else {
|
||||
window.Store.MDBackend = true;
|
||||
}
|
||||
|
||||
const _features = window.mR.findModule('FEATURE_CHANGE_EVENT')[0];
|
||||
if(_features) {
|
||||
window.Store.Features = _features.LegacyPhoneFeatures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Target options object description
|
||||
* @typedef {Object} TargetOptions
|
||||
* @property {string|number} module The name or a key of the target module to search
|
||||
* @property {number} index The index value of the target module
|
||||
* @property {string} function The function name to get from a module
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function to modify functions
|
||||
* @param {TargetOptions} target Options specifying the target function to search for modifying
|
||||
* @param {Function} callback Modified function
|
||||
*/
|
||||
window.injectToFunction = (target, callback) => {
|
||||
const module = typeof target.module === 'string'
|
||||
? window.mR.findModule(target.module)
|
||||
: window.mR.modules[target.module];
|
||||
const originalFunction = module[target.index][target.function];
|
||||
const modifiedFunction = (...args) => callback(originalFunction, ...args);
|
||||
module[target.index][target.function] = modifiedFunction;
|
||||
};
|
||||
|
||||
window.injectToFunction({ module: 'mediaTypeFromProtobuf', index: 0, function: 'mediaTypeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage ? null : func(...args); });
|
||||
|
||||
window.injectToFunction({ module: 'typeAttributeFromProtobuf', index: 0, function: 'typeAttributeFromProtobuf' }, (func, ...args) => { const [proto] = args; return proto.locationMessage || proto.groupInviteMessage ? 'text' : func(...args); });
|
||||
};
|
||||
|
||||
exports.LoadUtils = () => {
|
||||
window.WWebJS = {};
|
||||
|
||||
window.WWebJS.forwardMessage = async (chatId, msgId) => {
|
||||
let msg = window.Store.Msg.get(msgId);
|
||||
let chat = window.Store.Chat.get(chatId);
|
||||
|
||||
if (window.compareWwebVersions(window.Debug.VERSION, '>', '2.3000.0')) {
|
||||
return window.Store.ForwardUtils.forwardMessagesToChats([msg], [chat], false);
|
||||
} else {
|
||||
return chat.forwardMessages([msg]);
|
||||
}
|
||||
};
|
||||
|
||||
window.WWebJS.sendSeen = async (chatId) => {
|
||||
let chat = window.Store.Chat.get(chatId);
|
||||
if (chat !== undefined) {
|
||||
|
|
@ -332,15 +195,23 @@ exports.LoadUtils = () => {
|
|||
delete listOptions.list.footer;
|
||||
}
|
||||
|
||||
const botOptions = {};
|
||||
if (options.invokedBotWid) {
|
||||
botOptions.messageSecret = window.crypto.getRandomValues(new Uint8Array(32));
|
||||
botOptions.botMessageSecret = await window.Store.BotSecret.genBotMsgSecretFromMsgSecret(botOptions.messageSecret);
|
||||
botOptions.invokedBotWid = window.Store.WidFactory.createWid(options.invokedBotWid);
|
||||
botOptions.botPersonaId = window.Store.BotProfiles.BotProfileCollection.get(options.invokedBotWid).personaId;
|
||||
delete options.invokedBotWid;
|
||||
}
|
||||
|
||||
const meUser = window.Store.User.getMaybeMeUser();
|
||||
const isMD = window.Store.MDBackend;
|
||||
const newId = await window.Store.MsgKey.newId();
|
||||
|
||||
const newMsgId = new window.Store.MsgKey({
|
||||
from: meUser,
|
||||
to: chat.id,
|
||||
id: newId,
|
||||
participant: isMD && chat.id.isGroup() ? meUser : undefined,
|
||||
participant: chat.id.isGroup() ? meUser : undefined,
|
||||
selfDir: 'out',
|
||||
});
|
||||
|
||||
|
|
@ -370,9 +241,15 @@ exports.LoadUtils = () => {
|
|||
...vcardOptions,
|
||||
...buttonOptions,
|
||||
...listOptions,
|
||||
...botOptions,
|
||||
...extraOptions
|
||||
};
|
||||
|
||||
// Bot's won't reply if canonicalUrl is set (linking)
|
||||
if (botOptions) {
|
||||
delete message.canonicalUrl;
|
||||
}
|
||||
|
||||
await window.Store.SendMessage.addAndSendMsgToChat(chat, message);
|
||||
return window.Store.Msg.get(newMsgId._serialized);
|
||||
};
|
||||
|
|
@ -666,7 +543,7 @@ exports.LoadUtils = () => {
|
|||
window.WWebJS.getContact = async contactId => {
|
||||
const wid = window.Store.WidFactory.createWid(contactId);
|
||||
const contact = await window.Store.Contact.find(wid);
|
||||
const bizProfile = await window.Store.BusinessProfileCollection.fetchBizProfile(wid);
|
||||
const bizProfile = await window.Store.BusinessProfile.fetchBizProfile(wid);
|
||||
bizProfile.profileOptions && (contact.businessProfile = bizProfile);
|
||||
return window.WWebJS.getContactModel(contact);
|
||||
};
|
||||
|
|
@ -787,9 +664,8 @@ exports.LoadUtils = () => {
|
|||
};
|
||||
|
||||
window.WWebJS.sendChatstate = async (state, chatId) => {
|
||||
if (window.Store.MDBackend) {
|
||||
chatId = window.Store.WidFactory.createWid(chatId);
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case 'typing':
|
||||
await window.Store.ChatState.sendChatStateComposing(chatId);
|
||||
|
|
@ -1037,7 +913,7 @@ exports.LoadUtils = () => {
|
|||
let response;
|
||||
let result = [];
|
||||
|
||||
await window.Store.GroupMetadata.queryAndUpdate(groupWid);
|
||||
await window.Store.GroupQueryAndUpdate(groupWid);
|
||||
|
||||
if (!requesterIds?.length) {
|
||||
membershipRequests = group.groupMetadata.membershipApprovalRequests._models.map(({ id }) => id);
|
||||
|
|
@ -1131,4 +1007,5 @@ exports.LoadUtils = () => {
|
|||
if (response.messageSendResult === 'OK') return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
};
|
||||
|
|
@ -29,11 +29,8 @@ class LocalWebCache extends WebCache {
|
|||
}
|
||||
}
|
||||
|
||||
async persist(indexHtml) {
|
||||
// extract version from index (e.g. manifest-2.2206.9.json -> 2.2206.9)
|
||||
const version = indexHtml.match(/manifest-([\d\\.]+)\.json/)[1];
|
||||
if(!version) return;
|
||||
|
||||
async persist(indexHtml, version) {
|
||||
// version = (version+'').replace(/[^0-9.]/g,'');
|
||||
const filePath = path.join(this.path, `${version}.html`);
|
||||
fs.mkdirSync(this.path, { recursive: true });
|
||||
fs.writeFileSync(filePath, indexHtml);
|
||||
|
|
|
|||
Loading…
Reference in a new issue