Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Switch between QR or phone number pairing method #3180

Open
wants to merge 55 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
c1ae300
fix(webpack-exodus): Added the new patch
PurpShell Mar 14, 2024
db72385
fix(webpack-exodus): Fixed v2.3000.x and v2.24
PurpShell Apr 3, 2024
24196db
chore(webpack-exodus): eslint
PurpShell Apr 3, 2024
f001408
Merge branch 'main' into webpack-exodus
PurpShell Apr 3, 2024
eda1920
fix(webpack-exodus): Fixed Store
PurpShell Apr 3, 2024
467f6d2
Merge branch 'webpack-exodus' of https://github.com/pedroslopez/whats…
PurpShell Apr 3, 2024
21904ef
fix(webpack-exodus): slight store fix
PurpShell Apr 3, 2024
5969892
chore(webpack-exodus): fix example so that I don't crash on every load
PurpShell Apr 3, 2024
1df7e4f
chore: eslint fix
alechkos Apr 3, 2024
77f9c18
fix(webpack-exodus): prevent confusion with build systems
PurpShell Apr 3, 2024
75623c8
Merge branch 'webpack-exodus' of https://github.com/pedroslopez/whats…
PurpShell Apr 3, 2024
5b1e911
fix(webpack-exodus): Add proper groups module
PurpShell Apr 3, 2024
78dc055
fix(webpack-exodus): Fix order queries
PurpShell Apr 3, 2024
269e44e
fix(webpack-exodus): Bad export name
PurpShell Apr 9, 2024
85db3d2
feat(webpack-exodus): Re-injection, No more selectors, Pairing Code auth
PurpShell Apr 14, 2024
27621f1
fix(webpack-exodus): Eslint and better login/logout handling
PurpShell Apr 14, 2024
dd75967
fix(webpack-exodus): 2.24 compatibility
PurpShell Apr 14, 2024
e7ae65f
revert testing options
PurpShell Apr 14, 2024
cad33ec
chore(webpack-exodus): ESLint
PurpShell Apr 14, 2024
452b712
chore(webpack-exodus): final eslint tweak (did not commit with last c…
PurpShell Apr 14, 2024
b347aa6
Merge branch 'main' into webpack-exodus
alechkos Apr 22, 2024
fac0d81
style: fix broken link in readme file
alechkos Apr 22, 2024
53672d0
Merge branch 'main' into webpack-exodus
alechkos Apr 27, 2024
c1d8e04
fix(webpack-exodus): Fix forwarding messages
PurpShell Apr 28, 2024
86d5dc4
Merge branch 'webpack-exodus' of https://github.com/pedroslopez/whats…
PurpShell Apr 28, 2024
650cd0d
fix(webpack-exodus): finish forwarding function
PurpShell Apr 28, 2024
64491be
Making the ESLint god happy
tuyuribr May 14, 2024
975819d
Fix window.Store.ProfilePic.profilePicFind is not a function error (#…
seowzhenjun0126 May 14, 2024
e4c208c
Sanitize, improves and fixes
tuyuribr May 15, 2024
252ed8f
make ESList god happy
tuyuribr May 15, 2024
2798396
fix getInviteCode for group (#3007) (#3029)
themazim May 15, 2024
6df4eef
fixes
tuyuribr May 16, 2024
1ecdc42
Update LocalWebCache.js
tuyuribr May 21, 2024
96b4742
fix delete on 2.3000 (#3048)
jrocha May 24, 2024
bbb8d18
Implement bot invoking capabilities (#3009)
MatMercer May 25, 2024
752d9ae
Webpack exodus fix message_reaction event (#3099)
jrocha Jun 12, 2024
efc2bd0
message_reaction on example.js (#3102)
jrocha Jun 13, 2024
cd566f2
fix reactions on pvt chats (#3104)
jrocha Jun 14, 2024
4ddbb0d
New group function
tuyuribr Jul 2, 2024
8034e8f
new group settings
tuyuribr Jul 2, 2024
5a088a6
Merge branch 'main' into webpack-exodus
tuyuribr Jul 23, 2024
f361cde
Update Client.js
tuyuribr Jul 23, 2024
4494fa0
Switch pairing method between QR and phone number
MobCode100 Jul 2, 2024
cac19f2
Remove existing pairing code interval
MobCode100 Jul 3, 2024
113b2a9
Modify example.js
MobCode100 Jul 3, 2024
d14f094
Update docs
MobCode100 Jul 3, 2024
adbfb3d
Small improvements in inject function
MobCode100 Jul 3, 2024
c9359be
Merge branch 'main' into webpack-exodus
MobCode100 Jul 29, 2024
2a5af75
Merge branch 'remote-main' into webpack-exodus
MobCode100 Sep 16, 2024
cd9203d
Merge branch 'main' into webpack-exodus
alechkos Sep 18, 2024
8c420a7
Merge branch 'upstream-main' into webpack-exodus
MobCode100 Oct 1, 2024
b5c64b8
Fix ESLint
MobCode100 Oct 1, 2024
cd890cd
example.js correction
MobCode100 Oct 1, 2024
2c9d5ab
Merge remote-tracking branch 'upstream/main'
MobCode100 Oct 4, 2024
723b211
docs: fix docs for 'requestPairingCode' method
alechkos Nov 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions example.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ const client = new Client({
puppeteer: {
// args: ['--proxy-server=proxy-server-that-requires-authentication.example.com'],
headless: false,
}
},
// pairWithPhoneNumber: {
// phoneNumber: '96170100100' // Pair with phone number (format: <COUNTRY_CODE><PHONE_NUMBER>)
// }
});

// client initialize does not finish at ready now.
Expand All @@ -16,19 +19,13 @@ client.on('loading_screen', (percent, message) => {
console.log('LOADING SCREEN', percent, message);
});

// 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('code', (code) => {
console.log('Pairing code:',code);
});

client.on('authenticated', () => {
Expand Down
21 changes: 19 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,11 @@ declare namespace WAWebJS {
/**
* 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
* @param showNotification - Show notification to pair on phone number. Defaults to `true`
* @param intervalMs - The interval in milliseconds on how frequent to generate pairing code (WhatsApp default to 3 minutes). Defaults to `180000`
* @returns {Promise<string>} - Returns a pairing code in format "ABCDEFGH"
*/
requestPairingCode(phoneNumber: string, showNotification = true): Promise<string>
requestPairingCode(phoneNumber: string, showNotification?: boolean, intervalMs?: number): Promise<string>

/** Force reset of connection state for the client */
resetState(): Promise<void>
Expand Down Expand Up @@ -386,6 +387,13 @@ declare namespace WAWebJS {
qr: string
) => void): this

/** Emitted when the phone number pairing code is received */
on(event: 'code', listener: (
/** pairing code string
* @example `8W2WZ3TS` */
code: string
) => void): this

/** Emitted when a call is received */
on(event: 'call', listener: (
/** The call that started */
Expand Down Expand Up @@ -485,6 +493,15 @@ declare namespace WAWebJS {
ffmpegPath?: string,
/** Object with proxy autentication requirements @default: undefined */
proxyAuthentication?: {username: string, password: string} | undefined
/** Phone number pairing configuration. Refer the requestPairingCode function of Client.
* @default
* {
* phoneNumber: "",
* showNotification: true,
* intervalMs: 180000,
* }
*/
pairWithPhoneNumber?: {phoneNumber: string, showNotification?: boolean, intervalMs?: number}
}

export interface LocalWebCacheOptions {
Expand Down
102 changes: 65 additions & 37 deletions src/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class Client extends EventEmitter {
*/
async inject() {
await this.pupPage.waitForFunction('window.Debug?.VERSION != undefined', {timeout: this.options.authTimeoutMs});

const pairWithPhoneNumber = this.options.pairWithPhoneNumber;
const version = await this.getWWebVersion();
const isCometOrAbove = parseInt(version.split('.')?.[1]) >= 3000;

Expand Down Expand Up @@ -140,41 +140,55 @@ class Client extends EventEmitter {
return;
}

// Register qr events
let qrRetries = 0;
await exposeFunctionIfAbsent(this.pupPage, 'onQRChangedEvent', async (qr) => {
/**
* Emitted when a QR code is received
* @event Client#qr
* @param {string} qr QR Code
*/
this.emit(Events.QR_RECEIVED, qr);
if (this.options.qrMaxRetries > 0) {
qrRetries++;
if (qrRetries > this.options.qrMaxRetries) {
this.emit(Events.DISCONNECTED, 'Max qrcode retries reached');
await this.destroy();
// Register qr/code events
if(pairWithPhoneNumber.phoneNumber){
await exposeFunctionIfAbsent(this.pupPage, 'onCodeReceivedEvent', async (code) => {
/**
* Emitted when a pairing code is received
* @event Client#code
* @param {string} code Code
* @returns {string} Code that was just received
*/
this.emit(Events.CODE_RECEIVED, code);
return code;
});
this.requestPairingCode(pairWithPhoneNumber.phoneNumber,pairWithPhoneNumber.showNotification,pairWithPhoneNumber.intervalMs);
} else {
let qrRetries = 0;
await exposeFunctionIfAbsent(this.pupPage, 'onQRChangedEvent', async (qr) => {
/**
* Emitted when a QR code is received
* @event Client#qr
* @param {string} qr QR Code
*/
this.emit(Events.QR_RECEIVED, qr);
if (this.options.qrMaxRetries > 0) {
qrRetries++;
if (qrRetries > this.options.qrMaxRetries) {
this.emit(Events.DISCONNECTED, 'Max qrcode retries reached');
await this.destroy();
}
}
}
});
});


await this.pupPage.evaluate(async () => {
const registrationInfo = await window.AuthStore.RegistrationUtils.waSignalStore.getRegistrationInfo();
const noiseKeyPair = await window.AuthStore.RegistrationUtils.waNoiseInfo.get();
const staticKeyB64 = window.AuthStore.Base64Tools.encodeB64(noiseKeyPair.staticKeyPair.pubKey);
const identityKeyB64 = window.AuthStore.Base64Tools.encodeB64(registrationInfo.identityKeyPair.pubKey);
const advSecretKey = await window.AuthStore.RegistrationUtils.getADVSecretKey();
const platform = window.AuthStore.RegistrationUtils.DEVICE_PLATFORM;
const getQR = (ref) => ref + ',' + staticKeyB64 + ',' + identityKeyB64 + ',' + advSecretKey + ',' + platform;

window.onQRChangedEvent(getQR(window.AuthStore.Conn.ref)); // initial qr
window.AuthStore.Conn.on('change:ref', (_, ref) => { window.onQRChangedEvent(getQR(ref)); }); // future QR changes
});
await this.pupPage.evaluate(async () => {
const registrationInfo = await window.AuthStore.RegistrationUtils.waSignalStore.getRegistrationInfo();
const noiseKeyPair = await window.AuthStore.RegistrationUtils.waNoiseInfo.get();
const staticKeyB64 = window.AuthStore.Base64Tools.encodeB64(noiseKeyPair.staticKeyPair.pubKey);
const identityKeyB64 = window.AuthStore.Base64Tools.encodeB64(registrationInfo.identityKeyPair.pubKey);
const advSecretKey = await window.AuthStore.RegistrationUtils.getADVSecretKey();
const platform = window.AuthStore.RegistrationUtils.DEVICE_PLATFORM;
const getQR = (ref) => ref + ',' + staticKeyB64 + ',' + identityKeyB64 + ',' + advSecretKey + ',' + platform;

window.onQRChangedEvent(getQR(window.AuthStore.Conn.ref)); // initial qr
window.AuthStore.Conn.on('change:ref', (_, ref) => { window.onQRChangedEvent(getQR(ref)); }); // future QR changes
});
}
}

await exposeFunctionIfAbsent(this.pupPage, 'onAuthAppStateChangedEvent', async (state) => {
if (state == 'UNPAIRED_IDLE') {
if (state == 'UNPAIRED_IDLE' && !pairWithPhoneNumber.phoneNumber) {
// refresh qr code
window.Store.Cmd.refreshQR();
}
Expand Down Expand Up @@ -343,15 +357,29 @@ class Client extends EventEmitter {
/**
* Request authentication via pairing code instead of QR code
* @param {string} phoneNumber - Phone number in international, symbol-free format (e.g. 12025550108 for US, 551155501234 for Brazil)
* @param {boolean} showNotification - Show notification to pair on phone number
* @param {boolean} [showNotification = true] - Show notification to pair on phone number
* @param {number} [intervalMs = 180000] - The interval in milliseconds on how frequent to generate pairing code (WhatsApp default to 3 minutes)
* @returns {Promise<string>} - Returns a pairing code in format "ABCDEFGH"
*/
async requestPairingCode(phoneNumber, showNotification = true) {
return await this.pupPage.evaluate(async (phoneNumber, showNotification) => {
window.AuthStore.PairingCodeLinkUtils.setPairingType('ALT_DEVICE_LINKING');
await window.AuthStore.PairingCodeLinkUtils.initializeAltDeviceLinking();
return window.AuthStore.PairingCodeLinkUtils.startAltLinkingFlow(phoneNumber, showNotification);
}, phoneNumber, showNotification);
async requestPairingCode(phoneNumber, showNotification = true, intervalMs = 180000) {
return await this.pupPage.evaluate(async (phoneNumber, showNotification, intervalMs) => {
const getCode = async () => {
window.AuthStore.PairingCodeLinkUtils.setPairingType('ALT_DEVICE_LINKING');
await window.AuthStore.PairingCodeLinkUtils.initializeAltDeviceLinking();
return window.AuthStore.PairingCodeLinkUtils.startAltLinkingFlow(phoneNumber, showNotification);
};
if (window.codeInterval) {
clearInterval(window.codeInterval); // remove existing interval
}
window.codeInterval = setInterval(async () => {
if (window.AuthStore.AppState.state != 'UNPAIRED' && window.AuthStore.AppState.state != 'UNPAIRED_IDLE') {
clearInterval(window.codeInterval);
return;
}
window.onCodeReceivedEvent(await getCode());
}, intervalMs);
Comment on lines +371 to +380
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MobCode100 Is there a way we could get rid of this custom interval and use the WhatsApp one? Maybe there is an event that triggered once the code is expired and we can subscribe to this event?

Copy link
Author

@MobCode100 MobCode100 Oct 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anyway to find out? Using inspect element or some tool? I will do my own research on this later.

return window.onCodeReceivedEvent(await getCode());
}, phoneNumber, showNotification, intervalMs);
}

/**
Expand Down
8 changes: 7 additions & 1 deletion src/util/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ exports.DefaultOptions = {
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36',
ffmpegPath: 'ffmpeg',
bypassCSP: false,
proxyAuthentication: undefined
proxyAuthentication: undefined,
pairWithPhoneNumber: {
phoneNumber: '',
showNotification: true,
intervalMs: 180000,
},
};

/**
Expand Down Expand Up @@ -60,6 +65,7 @@ exports.Events = {
GROUP_MEMBERSHIP_REQUEST: 'group_membership_request',
GROUP_UPDATE: 'group_update',
QR_RECEIVED: 'qr',
CODE_RECEIVED: 'code',
LOADING_SCREEN: 'loading_screen',
DISCONNECTED: 'disconnected',
STATE_CHANGED: 'change_state',
Expand Down
Loading