@@ -93,168 +93,182 @@ class Client extends EventEmitter {
9393 * Private function
9494 */
9595 async inject ( ) {
96- await this . pupPage . waitForFunction ( 'window.Debug?.VERSION != undefined' , { timeout : this . options . authTimeoutMs } ) ;
96+ let hasReloaded = false ;
97+ const reloadHandler = async ( ) => {
98+ hasReloaded = true ;
99+ } ;
100+ try {
101+ this . pupPage . on ( 'framenavigated' , reloadHandler ) ;
97102
98- const version = await this . getWWebVersion ( ) ;
99- const isCometOrAbove = parseInt ( version . split ( '.' ) ?. [ 1 ] ) >= 3000 ;
103+ await this . pupPage . waitForFunction ( 'window.Debug?.VERSION != undefined' , { timeout : this . options . authTimeoutMs } ) ;
100104
101- if ( isCometOrAbove ) {
102- await this . pupPage . evaluate ( ExposeAuthStore ) ;
103- } else {
104- await this . pupPage . evaluate ( ExposeLegacyAuthStore , moduleRaid . toString ( ) ) ;
105- }
105+ const version = await this . getWWebVersion ( ) ;
106+ const isCometOrAbove = parseInt ( version . split ( '.' ) ?. [ 1 ] ) >= 3000 ;
106107
107- const needAuthentication = await this . pupPage . evaluate ( async ( ) => {
108- let state = window . AuthStore . AppState . state ;
109-
110- if ( state === 'OPENING' || state === 'UNLAUNCHED' || state === 'PAIRING' ) {
111- // wait till state changes
112- await new Promise ( r => {
113- window . AuthStore . AppState . on ( 'change:state' , function waitTillInit ( _AppState , state ) {
114- if ( state !== 'OPENING' && state !== 'UNLAUNCHED' && state !== 'PAIRING' ) {
115- window . AuthStore . AppState . off ( 'change:state' , waitTillInit ) ;
116- r ( ) ;
117- }
118- } ) ;
119- } ) ;
108+ if ( isCometOrAbove ) {
109+ await this . pupPage . evaluate ( ExposeAuthStore ) ;
110+ } else {
111+ await this . pupPage . evaluate ( ExposeLegacyAuthStore , moduleRaid . toString ( ) ) ;
120112 }
121- state = window . AuthStore . AppState . state ;
122- return state == 'UNPAIRED' || state == 'UNPAIRED_IDLE' ;
123- } ) ;
124113
125- if ( needAuthentication ) {
126- const { failed, failureEventPayload, restart } = await this . authStrategy . onAuthenticationNeeded ( ) ;
114+ const needAuthentication = await this . pupPage . evaluate ( async ( ) => {
115+ let state = window . AuthStore . AppState . state ;
116+
117+ if ( state === 'OPENING' || state === 'UNLAUNCHED' || state === 'PAIRING' ) {
118+ // wait till state changes
119+ await new Promise ( r => {
120+ window . AuthStore . AppState . on ( 'change:state' , function waitTillInit ( _AppState , state ) {
121+ if ( state !== 'OPENING' && state !== 'UNLAUNCHED' && state !== 'PAIRING' ) {
122+ window . AuthStore . AppState . off ( 'change:state' , waitTillInit ) ;
123+ r ( ) ;
124+ }
125+ } ) ;
126+ } ) ;
127+ }
128+ state = window . AuthStore . AppState . state ;
129+ return state == 'UNPAIRED' || state == 'UNPAIRED_IDLE' ;
130+ } ) ;
127131
128- if ( failed ) {
129- /**
130- * Emitted when there has been an error while trying to restore an existing session
131- * @event Client#auth_failure
132- * @param {string } message
133- */
134- this . emit ( Events . AUTHENTICATION_FAILURE , failureEventPayload ) ;
135- await this . destroy ( ) ;
136- if ( restart ) {
137- // session restore failed so try again but without session to force new authentication
138- return this . initialize ( ) ;
132+ if ( needAuthentication ) {
133+ const { failed, failureEventPayload, restart } = await this . authStrategy . onAuthenticationNeeded ( ) ;
134+
135+ if ( failed ) {
136+ /**
137+ * Emitted when there has been an error while trying to restore an existing session
138+ * @event Client#auth_failure
139+ * @param {string } message
140+ */
141+ this . emit ( Events . AUTHENTICATION_FAILURE , failureEventPayload ) ;
142+ await this . destroy ( ) ;
143+ if ( restart ) {
144+ // session restore failed so try again but without session to force new authentication
145+ return this . initialize ( ) ;
146+ }
147+ return ;
139148 }
140- return ;
141- }
142149
143- // Register qr events
144- let qrRetries = 0 ;
145- await exposeFunctionIfAbsent ( this . pupPage , 'onQRChangedEvent' , async ( qr ) => {
146- /**
147- * Emitted when a QR code is received
148- * @event Client#qr
149- * @param {string } qr QR Code
150- */
151- this . emit ( Events . QR_RECEIVED , qr ) ;
152- if ( this . options . qrMaxRetries > 0 ) {
153- qrRetries ++ ;
154- if ( qrRetries > this . options . qrMaxRetries ) {
155- this . emit ( Events . DISCONNECTED , 'Max qrcode retries reached' ) ;
156- await this . destroy ( ) ;
150+ // Register qr events
151+ let qrRetries = 0 ;
152+ await exposeFunctionIfAbsent ( this . pupPage , 'onQRChangedEvent' , async ( qr ) => {
153+ /**
154+ * Emitted when a QR code is received
155+ * @event Client#qr
156+ * @param {string } qr QR Code
157+ */
158+ this . emit ( Events . QR_RECEIVED , qr ) ;
159+ if ( this . options . qrMaxRetries > 0 ) {
160+ qrRetries ++ ;
161+ if ( qrRetries > this . options . qrMaxRetries ) {
162+ this . emit ( Events . DISCONNECTED , 'Max qrcode retries reached' ) ;
163+ await this . destroy ( ) ;
164+ }
157165 }
166+ } ) ;
167+
168+
169+ await this . pupPage . evaluate ( async ( ) => {
170+ const registrationInfo = await window . AuthStore . RegistrationUtils . waSignalStore . getRegistrationInfo ( ) ;
171+ const noiseKeyPair = await window . AuthStore . RegistrationUtils . waNoiseInfo . get ( ) ;
172+ const staticKeyB64 = window . AuthStore . Base64Tools . encodeB64 ( noiseKeyPair . staticKeyPair . pubKey ) ;
173+ const identityKeyB64 = window . AuthStore . Base64Tools . encodeB64 ( registrationInfo . identityKeyPair . pubKey ) ;
174+ const advSecretKey = await window . AuthStore . RegistrationUtils . getADVSecretKey ( ) ;
175+ const platform = window . AuthStore . RegistrationUtils . DEVICE_PLATFORM ;
176+ const getQR = ( ref ) => ref + ',' + staticKeyB64 + ',' + identityKeyB64 + ',' + advSecretKey + ',' + platform ;
177+
178+ window . onQRChangedEvent ( getQR ( window . AuthStore . Conn . ref ) ) ; // initial qr
179+ window . AuthStore . Conn . on ( 'change:ref' , ( _ , ref ) => { window . onQRChangedEvent ( getQR ( ref ) ) ; } ) ; // future QR changes
180+ } ) ;
181+ }
182+
183+ await exposeFunctionIfAbsent ( this . pupPage , 'onAuthAppStateChangedEvent' , async ( state ) => {
184+ if ( state == 'UNPAIRED_IDLE' ) {
185+ // refresh qr code
186+ window . Store . Cmd . refreshQR ( ) ;
158187 }
159188 } ) ;
160189
190+ await exposeFunctionIfAbsent ( this . pupPage , 'onAppStateHasSyncedEvent' , async ( ) => {
191+ const authEventPayload = await this . authStrategy . getAuthEventPayload ( ) ;
192+ /**
193+ * Emitted when authentication is successful
194+ * @event Client#authenticated
195+ */
196+ this . emit ( Events . AUTHENTICATED , authEventPayload ) ;
161197
162- await this . pupPage . evaluate ( async ( ) => {
163- const registrationInfo = await window . AuthStore . RegistrationUtils . waSignalStore . getRegistrationInfo ( ) ;
164- const noiseKeyPair = await window . AuthStore . RegistrationUtils . waNoiseInfo . get ( ) ;
165- const staticKeyB64 = window . AuthStore . Base64Tools . encodeB64 ( noiseKeyPair . staticKeyPair . pubKey ) ;
166- const identityKeyB64 = window . AuthStore . Base64Tools . encodeB64 ( registrationInfo . identityKeyPair . pubKey ) ;
167- const advSecretKey = await window . AuthStore . RegistrationUtils . getADVSecretKey ( ) ;
168- const platform = window . AuthStore . RegistrationUtils . DEVICE_PLATFORM ;
169- const getQR = ( ref ) => ref + ',' + staticKeyB64 + ',' + identityKeyB64 + ',' + advSecretKey + ',' + platform ;
198+ const injected = await this . pupPage . evaluate ( async ( ) => {
199+ return typeof window . Store !== 'undefined' && typeof window . WWebJS !== 'undefined' ;
200+ } ) ;
201+
202+ if ( ! injected ) {
203+ if ( this . options . webVersionCache . type === 'local' && this . currentIndexHtml ) {
204+ const { type : webCacheType , ... webCacheOptions } = this . options . webVersionCache ;
205+ const webCache = WebCacheFactory . createWebCache ( webCacheType , webCacheOptions ) ;
170206
171- window . onQRChangedEvent ( getQR ( window . AuthStore . Conn . ref ) ) ; // initial qr
172- window . AuthStore . Conn . on ( 'change:ref' , ( _ , ref ) => { window . onQRChangedEvent ( getQR ( ref ) ) ; } ) ; // future QR changes
173- } ) ;
174- }
207+ await webCache . persist ( this . currentIndexHtml , version ) ;
208+ }
175209
176- await exposeFunctionIfAbsent ( this . pupPage , 'onAuthAppStateChangedEvent' , async ( state ) => {
177- if ( state == 'UNPAIRED_IDLE' ) {
178- // refresh qr code
179- window . Store . Cmd . refreshQR ( ) ;
180- }
181- } ) ;
210+ if ( isCometOrAbove ) {
211+ await this . pupPage . evaluate ( ExposeStore ) ;
212+ } else {
213+ // make sure all modules are ready before injection
214+ // 2 second delay after authentication makes sense and does not need to be made dyanmic or removed
215+ await new Promise ( r => setTimeout ( r , 2000 ) ) ;
216+ await this . pupPage . evaluate ( ExposeLegacyStore ) ;
217+ }
182218
183- await exposeFunctionIfAbsent ( this . pupPage , 'onAppStateHasSyncedEvent' , async ( ) => {
184- const authEventPayload = await this . authStrategy . getAuthEventPayload ( ) ;
185- /**
186- * Emitted when authentication is successful
187- * @event Client#authenticated
188- */
189- this . emit ( Events . AUTHENTICATED , authEventPayload ) ;
219+ // Check window.Store Injection
220+ await this . pupPage . waitForFunction ( 'window.Store != undefined' ) ;
221+
222+ /**
223+ * Current connection information
224+ * @type {ClientInfo }
225+ */
226+ this . info = new ClientInfo ( this , await this . pupPage . evaluate ( ( ) => {
227+ return { ...window . Store . Conn . serialize ( ) , wid : window . Store . User . getMeUser ( ) } ;
228+ } ) ) ;
190229
191- const injected = await this . pupPage . evaluate ( async ( ) => {
192- return typeof window . Store !== 'undefined' && typeof window . WWebJS !== 'undefined' ;
193- } ) ;
230+ this . interface = new InterfaceController ( this ) ;
194231
195- if ( ! injected ) {
196- if ( this . options . webVersionCache . type === 'local' && this . currentIndexHtml ) {
197- const { type : webCacheType , ...webCacheOptions } = this . options . webVersionCache ;
198- const webCache = WebCacheFactory . createWebCache ( webCacheType , webCacheOptions ) ;
199-
200- await webCache . persist ( this . currentIndexHtml , version ) ;
201- }
232+ //Load util functions (serializers, helper functions)
233+ await this . pupPage . evaluate ( LoadUtils ) ;
202234
203- if ( isCometOrAbove ) {
204- await this . pupPage . evaluate ( ExposeStore ) ;
205- } else {
206- // make sure all modules are ready before injection
207- // 2 second delay after authentication makes sense and does not need to be made dyanmic or removed
208- await new Promise ( r => setTimeout ( r , 2000 ) ) ;
209- await this . pupPage . evaluate ( ExposeLegacyStore ) ;
235+ await this . attachEventListeners ( ) ;
210236 }
211-
212- // Check window.Store Injection
213- await this . pupPage . waitForFunction ( 'window.Store != undefined' ) ;
214-
215237 /**
216- * Current connection information
217- * @type { ClientInfo }
238+ * Emitted when the client has initialized and is ready to receive messages.
239+ * @event Client#ready
218240 */
219- this . info = new ClientInfo ( this , await this . pupPage . evaluate ( ( ) => {
220- return { ...window . Store . Conn . serialize ( ) , wid : window . Store . User . getMeUser ( ) } ;
221- } ) ) ;
222-
223- this . interface = new InterfaceController ( this ) ;
224-
225- //Load util functions (serializers, helper functions)
226- await this . pupPage . evaluate ( LoadUtils ) ;
227-
228- await this . attachEventListeners ( ) ;
229- }
230- /**
231- * Emitted when the client has initialized and is ready to receive messages.
232- * @event Client#ready
233- */
234- this . emit ( Events . READY ) ;
235- this . authStrategy . afterAuthReady ( ) ;
236- } ) ;
237- let lastPercent = null ;
238- await exposeFunctionIfAbsent ( this . pupPage , 'onOfflineProgressUpdateEvent' , async ( percent ) => {
239- if ( lastPercent !== percent ) {
240- lastPercent = percent ;
241- this . emit ( Events . LOADING_SCREEN , percent , 'WhatsApp' ) ; // Message is hardcoded as "WhatsApp" for now
242- }
243- } ) ;
244- await exposeFunctionIfAbsent ( this . pupPage , 'onLogoutEvent' , async ( ) => {
245- this . lastLoggedOut = true ;
246- await this . pupPage . waitForNavigation ( { waitUntil : 'load' , timeout : 5000 } ) . catch ( ( _ ) => _ ) ;
247- } ) ;
248- await this . pupPage . evaluate ( ( ) => {
249- window . AuthStore . AppState . on ( 'change:state' , ( _AppState , state ) => { window . onAuthAppStateChangedEvent ( state ) ; } ) ;
250- window . AuthStore . AppState . on ( 'change:hasSynced' , ( ) => { window . onAppStateHasSyncedEvent ( ) ; } ) ;
251- window . AuthStore . Cmd . on ( 'offline_progress_update' , ( ) => {
252- window . onOfflineProgressUpdateEvent ( window . AuthStore . OfflineMessageHandler . getOfflineDeliveryProgress ( ) ) ;
241+ this . emit ( Events . READY ) ;
242+ this . authStrategy . afterAuthReady ( ) ;
243+ } ) ;
244+ let lastPercent = null ;
245+ await exposeFunctionIfAbsent ( this . pupPage , 'onOfflineProgressUpdateEvent' , async ( percent ) => {
246+ if ( lastPercent !== percent ) {
247+ lastPercent = percent ;
248+ this . emit ( Events . LOADING_SCREEN , percent , 'WhatsApp' ) ; // Message is hardcoded as "WhatsApp" for now
249+ }
253250 } ) ;
254- window . AuthStore . Cmd . on ( 'logout' , async ( ) => {
255- await window . onLogoutEvent ( ) ;
251+ await exposeFunctionIfAbsent ( this . pupPage , 'onLogoutEvent' , async ( ) => {
252+ this . lastLoggedOut = true ;
253+ await this . pupPage . waitForNavigation ( { waitUntil : 'load' , timeout : 5000 } ) . catch ( ( _ ) => _ ) ;
256254 } ) ;
257- } ) ;
255+ await this . pupPage . evaluate ( ( ) => {
256+ window . AuthStore . AppState . on ( 'change:state' , ( _AppState , state ) => { window . onAuthAppStateChangedEvent ( state ) ; } ) ;
257+ window . AuthStore . AppState . on ( 'change:hasSynced' , ( ) => { window . onAppStateHasSyncedEvent ( ) ; } ) ;
258+ window . AuthStore . Cmd . on ( 'offline_progress_update' , ( ) => {
259+ window . onOfflineProgressUpdateEvent ( window . AuthStore . OfflineMessageHandler . getOfflineDeliveryProgress ( ) ) ;
260+ } ) ;
261+ window . AuthStore . Cmd . on ( 'logout' , async ( ) => {
262+ await window . onLogoutEvent ( ) ;
263+ } ) ;
264+ } ) ;
265+
266+ } catch ( err ) {
267+ if ( ! hasReloaded )
268+ throw err ;
269+ } finally {
270+ this . pupPage . off ( 'framenavigated' , reloadHandler ) ;
271+ }
258272 }
259273
260274 /**
0 commit comments