Skip to content

Commit 7549ae4

Browse files
Alexey Poliakovclaude
andcommitted
feat: re-apply custom patches on top of upstream/main
- puppeteer-extra with stealth and adblocker plugins - require() error wrapper to prevent crashes - AB-flag conditional prefetch loadables with 10s delay - auto-dismiss new version modal (multi-language) - LID resolution fallback in getChat via WAWebContactSyncUtils - CODE_FAILED event - messageSecret optional typing fix Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1a9a1b7 commit 7549ae4

5 files changed

Lines changed: 124 additions & 6 deletions

File tree

index.d.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,11 @@ declare namespace WAWebJS {
714714
webVersion?: string;
715715
/** Determines how to retrieve the WhatsApp Web version specified in options.webVersion. */
716716
webVersionCache?: WebCacheOptions;
717+
/**
718+
* Enable the stealth version of wwebjs, uses puppeteer-stealth and adblocker
719+
* @default false
720+
*/
721+
stealth?: boolean;
717722
/** How many times should the qrcode be refreshed before giving up
718723
* @default 0 (disabled) */
719724
qrMaxRetries?: number;
@@ -1025,6 +1030,7 @@ declare namespace WAWebJS {
10251030
GROUP_UPDATE = 'group_update',
10261031
QR_RECEIVED = 'qr',
10271032
CODE_RECEIVED = 'code',
1033+
CODE_FAILED = 'code_failed',
10281034
LOADING_SCREEN = 'loading_screen',
10291035
CALL = 'call',
10301036
DISCONNECTED = 'disconnected',
@@ -1415,7 +1421,7 @@ declare namespace WAWebJS {
14151421
* The custom message secret, can be used as a poll ID
14161422
* @note It has to be a unique vector with a length of 32
14171423
*/
1418-
messageSecret: Array<number> | undefined;
1424+
messageSecret?: Array<number>;
14191425
}
14201426

14211427
/** Represents a Poll on WhatsApp */
@@ -1453,7 +1459,7 @@ declare namespace WAWebJS {
14531459
* The custom message secret, can be used as an event ID
14541460
* @note It has to be a unique vector with a length of 32
14551461
*/
1456-
messageSecret: Array<number> | undefined;
1462+
messageSecret?: Array<number>;
14571463
}
14581464

14591465
/** Represents a ScheduledEvent on WhatsApp */

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
"mime": "^3.0.0",
4040
"node-fetch": "^2.6.9",
4141
"node-webpmux": "3.1.7",
42+
"puppeteer-extra": "^3.3.6",
43+
"puppeteer-extra-plugin-adblocker": "^2.13.6",
44+
"puppeteer-extra-plugin-stealth": "^2.11.2",
4245
"puppeteer": "^24.31.0"
4346
},
4447
"devDependencies": {

src/Client.js

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
'use strict';
22

33
const EventEmitter = require('events');
4-
const puppeteer = require('puppeteer');
4+
const puppeteer = require('puppeteer-extra');
5+
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
6+
const AdblockerPlugin = require('puppeteer-extra-plugin-adblocker');
57

68
const Util = require('./util/Util');
79
const InterfaceController = require('./util/InterfaceController');
@@ -142,6 +144,18 @@ class Client extends EventEmitter {
142144

143145
await this.pupPage.evaluate(ExposeAuthStore);
144146

147+
await this.pupPage.evaluate(() => {
148+
const originalRequire = window.require;
149+
window.require = (...args) => {
150+
try {
151+
return originalRequire(...args);
152+
} catch (error) {
153+
console.error('Error in require:', error, 'Args:', args);
154+
return {};
155+
}
156+
};
157+
});
158+
145159
const needAuthentication = await this.pupPage.evaluate(async () => {
146160
let state = window.require('WAWebSocketModel').Socket.state;
147161

@@ -322,6 +336,18 @@ class Client extends EventEmitter {
322336
await webCache.persist(this.currentIndexHtml, version);
323337
}
324338

339+
await new Promise((r) => setTimeout(r, 10000)); // Avoid error "Dropping db read operation due to logout"
340+
await this.pupPage.evaluate(async () => {
341+
const targetABFlag = 'wa_web_disable_prefetch_loadables';
342+
const ABPrefetchLoadablesExists = !!(await window.require('WAWebABPropsConfigs').ABPropConfigs[targetABFlag]);
343+
if (ABPrefetchLoadablesExists) {
344+
const isUsingABPrefetchLoadables = await window.require('WAWebABProps').getABPropConfigValue(targetABFlag);
345+
if (isUsingABPrefetchLoadables) {
346+
await window.require('WAWebPrefetchLoadables')();
347+
}
348+
}
349+
});
350+
325351
//Load util functions (serializers, helper functions)
326352
await this.pupPage.evaluate(LoadUtils);
327353

@@ -373,6 +399,7 @@ class Client extends EventEmitter {
373399
*/
374400
this.emit(Events.READY);
375401
this.authStrategy.afterAuthReady();
402+
this.clickNewVersionModalButton();
376403
},
377404
);
378405
let lastPercent = null;
@@ -422,6 +449,50 @@ class Client extends EventEmitter {
422449
});
423450
}
424451

452+
/**
453+
* Clicks on new version modal button to close it
454+
*/
455+
async clickNewVersionModalButton() {
456+
try {
457+
const modalSelector = 'div[data-animate-modal-popup="true"]';
458+
const modalExists = await this.pupPage
459+
.waitForSelector(modalSelector, {
460+
timeout: 30000,
461+
visible: true,
462+
})
463+
.then(() => true)
464+
.catch(() => false);
465+
if (!modalExists) {
466+
return;
467+
}
468+
const buttonSelector = `${modalSelector} button div:not(:empty)`;
469+
const buttonText = [
470+
'Продолжить',
471+
'Continue',
472+
'Continuar',
473+
'Continuare',
474+
'Continuez',
475+
'Продовжити',
476+
];
477+
await this.pupPage.evaluate(
478+
(selector, buttonText) => {
479+
const elements = document.querySelectorAll(selector);
480+
for (const element of elements) {
481+
const text = element.textContent.trim();
482+
if (buttonText.includes(text)) {
483+
element.click();
484+
return;
485+
}
486+
}
487+
},
488+
buttonSelector,
489+
buttonText,
490+
);
491+
} catch (error) {
492+
console.error('Error clicking new version modal button:', error);
493+
}
494+
}
495+
425496
/**
426497
* Sets up events and requirements, kicks off authentication request
427498
*/
@@ -458,6 +529,15 @@ class Client extends EventEmitter {
458529
// navigator.webdriver fix
459530
browserArgs.push('--disable-blink-features=AutomationControlled');
460531

532+
if (this.options.stealth) {
533+
const stealth = StealthPlugin();
534+
stealth.enabledEvasions.delete('iframe.contentWindow');
535+
stealth.enabledEvasions.delete('media.codecs');
536+
stealth.enabledEvasions.delete('user-agent-override');
537+
puppeteer.use(stealth);
538+
puppeteer.use(AdblockerPlugin({ blockTrackers: true }));
539+
}
540+
461541
browser = await puppeteer.launch({
462542
...puppeteerOpts,
463543
args: browserArgs,

src/util/Constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ exports.Events = {
6868
GROUP_UPDATE: 'group_update',
6969
QR_RECEIVED: 'qr',
7070
CODE_RECEIVED: 'code',
71+
CODE_FAILED: 'code_failed',
7172
LOADING_SCREEN: 'loading_screen',
7273
DISCONNECTED: 'disconnected',
7374
STATE_CHANGED: 'change_state',

src/util/Injected/Utils.js

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -861,13 +861,41 @@ exports.LoadUtils = () => {
861861
chat = null;
862862
}
863863
} else {
864-
chat =
865-
window.require('WAWebCollections').Chat.get(chatWid) ||
866-
(
864+
chat = window.require('WAWebCollections').Chat.get(chatWid);
865+
if (!chat) {
866+
chat = (
867867
await window
868868
.require('WAWebFindChatAction')
869869
.findOrCreateLatestChat(chatWid)
870+
.catch(() => null)
870871
)?.chat;
872+
}
873+
if (!chat) {
874+
try {
875+
const query = window
876+
.require('WAWebContactSyncUtils')
877+
.constructUsyncDeltaQuery([
878+
{
879+
type: 'add',
880+
phoneNumber: chatWid.user,
881+
},
882+
]);
883+
const result = await query.execute();
884+
if (result?.list?.[0]?.lid) {
885+
const chatLid = window
886+
.require('WAWebWidFactory')
887+
.createWid(result.list[0].lid);
888+
chat = (
889+
await window
890+
.require('WAWebFindChatAction')
891+
.findOrCreateLatestChat(chatLid)
892+
.catch(() => null)
893+
)?.chat;
894+
}
895+
} catch (e) {
896+
// LID resolution failed, chat remains undefined
897+
}
898+
}
871899
}
872900

873901
return getAsModel && chat

0 commit comments

Comments
 (0)