@open-wa/wa-automate 4.30.10 → 4.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/setup.js CHANGED
@@ -44,242 +44,17 @@ const events_1 = require("../controllers/events");
44
44
  const is_url_superb_1 = __importDefault(require("is-url-superb"));
45
45
  const path = __importStar(require("path"));
46
46
  const logging_1 = require("../logging/logging");
47
+ const cli_options_1 = require("./cli-options");
47
48
  let checkUrl = url => typeof url === 'string' ? (0, is_url_superb_1.default)(url) : false;
48
49
  const configWithCases = (0, fs_extra_1.readJsonSync)(path.join(__dirname, '../../bin/config-schema.json'));
49
- const optionList = [{
50
- name: 'no-api',
51
- default: false,
52
- alias: 'n',
53
- type: Boolean,
54
- description: "Don't expose the api. This may be useful if you just want to set the webhooks."
55
- }, {
56
- name: 'bot-press-url',
57
- alias: 'b',
58
- type: String,
59
- typeLabel: '{blue {underline http://localhost:3000/api/v1/bots/cool-bot}}',
60
- description: "The Botpress URL that ends with your bot id."
61
- }, {
62
- name: 'twilio-webhook',
63
- alias: 't',
64
- type: String,
65
- typeLabel: '{blue {underline http://localhost:5555/incoming}}',
66
- description: "Send twillio payloads to this URL. EASY API will also parse and processes twillio response message payloads."
67
- }, {
68
- name: 'chatwoot-url',
69
- type: String,
70
- typeLabel: '{blue {underline http://localhost:3000/api/v1/accounts/3/inboxes/1}}',
71
- description: "The URL of the specific Chatwoot inbox you set up for this session"
72
- }, {
73
- name: 'chatwoot-api-access-token',
74
- type: String,
75
- typeLabel: '{blue {underline mEEwUGEEML2ZThMm252rLg1M}}',
76
- description: "The access token of the specific Chatwoot inbox you set up for this session"
77
- },
78
- {
79
- name: 'port',
80
- alias: 'p',
81
- default: 8002,
82
- type: Number,
83
- typeLabel: '{blue {underline 8080}}',
84
- description: "Set the port for the api. Default to 8002."
85
- },
86
- {
87
- name: 'api-host',
88
- type: String,
89
- typeLabel: '{yellow {underline localhost}}',
90
- description: "The easy API may be sitting behind a reverse proxy. In this case set --api-host in order to make sure the api docs and api explorer are working properly. You will need to include the protocol as well."
91
- },
92
- {
93
- name: 'host',
94
- alias: 'h',
95
- default: 'localhost',
96
- type: String,
97
- typeLabel: '{red {underline localhost}}',
98
- description: "Set the hostname for the api documantation and statistics. Overrides --api-host. Default: localhost."
99
- },
100
- {
101
- name: 'webhook',
102
- alias: 'w',
103
- type: String,
104
- typeLabel: '{yellow {underline https://webhook.site/....}}',
105
- description: "Webhook to use for the listeners."
106
- },
107
- {
108
- name: 'ev',
109
- alias: 'e',
110
- type: String,
111
- typeLabel: '{green {underline https://webhook.site/....}}',
112
- description: "Send launch events to this URL."
113
- },
114
- {
115
- name: 'ef',
116
- type: String,
117
- //@ts-ignore
118
- default: ["qr", "STARTUP"],
119
- isMultiple: true,
120
- typeLabel: '{blueBright {underline qr,STARTUP}}',
121
- description: "Filters which namespaces trigger the webhook set in -e/--ev."
122
- },
123
- {
124
- name: 'allow-session-data-wh',
125
- alias: 'x',
126
- default: false,
127
- type: Boolean,
128
- description: "By default, if you set -e flag, the session data is not transferred to the webhook as it is extremely sensitive data. In order to bypass this security measure, use this flag."
129
- },
130
- {
131
- name: 'key',
132
- alias: 'k',
133
- type: String,
134
- typeLabel: '{redBright {underline apikey}}',
135
- description: "Specify an api key to use as a check for all requests. If you add -k by itself, a key will be autogenerated for you."
136
- },
137
- {
138
- name: 'config',
139
- alias: 'c',
140
- type: String,
141
- typeLabel: '{yellowBright {underline ./config.json}}',
142
- description: "The relative json file that contains the config. By default the system will look for config.json which will override any config variables set. Default: './config.json'."
143
- },
144
- {
145
- name: 'session',
146
- alias: 's',
147
- type: String,
148
- typeLabel: '{magentaBright {underline BASE64}}',
149
- description: "A base64 string representing the session data."
150
- },
151
- {
152
- name: 'keep-alive',
153
- alias: 'a',
154
- type: Boolean,
155
- description: "If true, the system will force the session to refocus in this process. This will prevent you from opening a session elsewhere."
156
- },
157
- {
158
- name: 'use-session-id-in-path',
159
- alias: 'i',
160
- type: Boolean,
161
- description: "If true, all API paths will include the session id. default to false and the default session Id is 'session'."
162
- },
163
- {
164
- name: 'generate-api-docs',
165
- alias: 'd',
166
- type: Boolean,
167
- default: true,
168
- description: "Generate postman collection and expose api docs to open in browser."
169
- },
170
- {
171
- name: 'session-data-only',
172
- alias: 'o',
173
- type: Boolean,
174
- description: "Kill the process when the session data is saved.",
175
- default: false
176
- },
177
- {
178
- name: 'skip-save-postman-collection',
179
- type: Boolean,
180
- description: "Don't save the postman collection.",
181
- default: false
182
- },
183
- {
184
- name: 'headful',
185
- type: Boolean,
186
- description: "Show the browser window on your machine.",
187
- default: false
188
- },
189
- {
190
- name: 'headful',
191
- type: Boolean,
192
- description: "Pre authenticate documentation site [High security risk]."
193
- },
194
- {
195
- name: 'stats',
196
- type: Boolean,
197
- description: "Exposes API swagger-statistics.",
198
- default: false
199
- },
200
- {
201
- name: 'pre-auth-docs',
202
- type: Boolean,
203
- description: "Grab config options from the environment variables.",
204
- default: false
205
- },
206
- {
207
- name: 'no-kill-on-logout',
208
- type: Boolean,
209
- description: "Keeps the process alive when host account logs out of session. default is false",
210
- default: false
211
- },
212
- {
213
- name: 'debug',
214
- type: Boolean,
215
- description: "Print out the CLI flag values and the WA_* env vars. default is false",
216
- default: false
217
- },
218
- {
219
- name: 'cors',
220
- type: Boolean,
221
- description: "Enable all cors requests",
222
- default: false
223
- },
224
- {
225
- name: 'socket',
226
- type: Boolean,
227
- description: "Expose a socket.io middleware on the server.",
228
- default: false
229
- },
230
- {
231
- name: 'license-key',
232
- alias: 'l',
233
- type: String,
234
- typeLabel: '{yellowBright {underline B2BJ4JFB-2UN2J3ND-2J5I.....}}',
235
- description: "The license key you want to use for this server. License keys are used to unlock features. Learn more here https://github.com/open-wa/wa-automate-nodejs#license-key"
236
- },
237
- {
238
- name: 'ready-webhook',
239
- type: String,
240
- typeLabel: '{yellow {underline https://webhook.site/....}}',
241
- description: "Webhook that fires when the EASY API is completely ready"
242
- },
243
- {
244
- name: 'on-call',
245
- type: String,
246
- typeLabel: '{yellow {underline "Please do not call this number"}}',
247
- description: "A default message to send to any number that is trying to call the host account"
248
- },
249
- {
250
- name: 'auto-reject',
251
- type: Boolean,
252
- description: "Automatically reject incoming phone and video calls to the host account."
253
- },
254
- {
255
- name: 'emit-unread',
256
- type: Boolean,
257
- description: "Emit all unread messages via onMessage webhooks on launch.",
258
- default: false
259
- },
260
- {
261
- name: 'skip-url-check',
262
- type: Boolean,
263
- description: "Don't validate webhook URLs. Enables use of non-FQDNs."
264
- },
265
- {
266
- name: 'tunnel',
267
- type: Boolean,
268
- description: "Expose a tunnel to your EASY API session - this is for testing and it is unsecured."
269
- },
270
- {
271
- name: 'help',
272
- description: 'Print this usage guide.'
273
- }
274
- ];
275
- exports.optionKeys = optionList.map(({ name }) => (0, tools_1.camelize)(name));
276
- exports.optionKeysWithDefalts = [...optionList.filter(o => o.hasOwnProperty('default')).map(({ name }) => (0, tools_1.camelize)(name)), 'popup'];
50
+ exports.optionKeys = cli_options_1.optionList.map(({ name }) => (0, tools_1.camelize)(name));
51
+ exports.optionKeysWithDefalts = [...cli_options_1.optionList.filter(o => o.hasOwnProperty('default')).map(({ name }) => (0, tools_1.camelize)(name)), 'popup'];
277
52
  exports.PrimitiveConverter = {
278
53
  Number: 1,
279
54
  Boolean: true,
280
55
  String: "hello"
281
56
  };
282
- exports.cliOptionNames = optionList.reduce((acc, c) => {
57
+ exports.cliOptionNames = cli_options_1.optionList.reduce((acc, c) => {
283
58
  if (!c.type)
284
59
  return acc;
285
60
  acc[(0, tools_1.camelize)(c.name)] = typeof exports.PrimitiveConverter[c.type.name];
@@ -299,7 +74,7 @@ const meowFlags = () => {
299
74
  };
300
75
  });
301
76
  const res = {};
302
- optionList.map(option => {
77
+ cli_options_1.optionList.map(option => {
303
78
  var _a, _b;
304
79
  res[(0, tools_1.camelize)(option.name)] = Object.assign(Object.assign({}, option), {
305
80
  //@ts-ignore
@@ -314,7 +89,7 @@ exports.helptext = (0, command_line_usage_1.default)([{
314
89
  },
315
90
  {
316
91
  header: '',
317
- optionList
92
+ optionList: cli_options_1.optionList
318
93
  },
319
94
  {
320
95
  header: "Session config flags",
@@ -383,7 +158,7 @@ const cli = () => {
383
158
  * 3. CLI flags
384
159
  */
385
160
  const nonCliConfigs = Object.assign(Object.assign({}, (0, exports.envArgs)()), ((0, exports.configFile)(_cli.flags.config) || {}));
386
- optionList.filter(option => option.default);
161
+ cli_options_1.optionList.filter(option => option.default);
387
162
  const cliConfig = Object.assign(Object.assign(Object.assign({ sessionId: "session" }, nonCliConfigs), _cli.flags), exports.optionKeysWithDefalts.reduce((p, c) => nonCliConfigs.hasOwnProperty(c) ? Object.assign(Object.assign({}, p), { [c]: nonCliConfigs[c] }) : p, {}));
388
163
  //firstly set up logger
389
164
  if (cliConfig === null || cliConfig === void 0 ? void 0 : cliConfig.logging) {
@@ -3,13 +3,13 @@ import { Spin } from './events';
3
3
  import { ConfigObject } from '../api/model';
4
4
  import { Page } from 'puppeteer';
5
5
  /**
6
+ * isAuthenticated
6
7
  * Validates if client is authenticated
7
8
  * @returns true if is authenticated, false otherwise
8
9
  * @param waPage
9
10
  */
10
11
  export declare const isAuthenticated: (waPage: Page) => Promise<unknown>;
11
12
  export declare const needsToScan: (waPage: Page) => Observable<unknown>;
12
- export declare const isInsideChat: (waPage: Page) => Observable<boolean>;
13
13
  export declare const waitForRipeSession: (waPage: Page) => Promise<boolean>;
14
14
  export declare const sessionDataInvalid: (waPage: Page) => Promise<string>;
15
15
  export declare const phoneIsOutOfReach: (waPage: Page) => Promise<boolean>;
@@ -27,6 +27,10 @@ export declare class QRManager {
27
27
  grabAndEmit(qrData: any, waPage: Page, config: ConfigObject, spinner: Spin): Promise<void>;
28
28
  smartQr(waPage: Page, config?: ConfigObject, spinner?: Spin): Promise<boolean | void | string>;
29
29
  emitFirst(waPage: Page, config?: ConfigObject, spinner?: Spin): Promise<void>;
30
+ /**
31
+ * Wait 10 seconds for the qr element to show.
32
+ * If it doesn't show up within 10 seconds then assume the session is authed already or blocked therefore ignore and return promise
33
+ */
30
34
  waitFirstQr(waPage: Page, config?: ConfigObject, spinner?: Spin): Promise<void>;
31
35
  }
32
36
  export declare const qrManager: QRManager;
@@ -31,7 +31,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
31
31
  return (mod && mod.__esModule) ? mod : { "default": mod };
32
32
  };
33
33
  Object.defineProperty(exports, "__esModule", { value: true });
34
- exports.qrManager = exports.QRManager = exports.phoneIsOutOfReach = exports.sessionDataInvalid = exports.waitForRipeSession = exports.isInsideChat = exports.needsToScan = exports.isAuthenticated = void 0;
34
+ exports.qrManager = exports.QRManager = exports.phoneIsOutOfReach = exports.sessionDataInvalid = exports.waitForRipeSession = exports.needsToScan = exports.isAuthenticated = void 0;
35
35
  const qrcode = __importStar(require("qrcode-terminal"));
36
36
  const rxjs_1 = require("rxjs");
37
37
  const events_1 = require("./events");
@@ -41,11 +41,12 @@ const browser_1 = require("./browser");
41
41
  const axios_1 = __importDefault(require("axios"));
42
42
  const logging_1 = require("../logging/logging");
43
43
  /**
44
+ * isAuthenticated
44
45
  * Validates if client is authenticated
45
46
  * @returns true if is authenticated, false otherwise
46
47
  * @param waPage
47
48
  */
48
- const isAuthenticated = (waPage) => (0, rxjs_1.race)((0, exports.needsToScan)(waPage), (0, exports.isInsideChat)(waPage), (0, exports.sessionDataInvalid)(waPage)).toPromise();
49
+ const isAuthenticated = (waPage) => (0, rxjs_1.race)((0, exports.needsToScan)(waPage), isInsideChat(waPage), (0, exports.sessionDataInvalid)(waPage)).toPromise();
49
50
  exports.isAuthenticated = isAuthenticated;
50
51
  const needsToScan = (waPage) => {
51
52
  return (0, rxjs_1.from)(new Promise((resolve) => __awaiter(void 0, void 0, void 0, function* () {
@@ -72,7 +73,11 @@ const isInsideChat = (waPage) => {
72
73
  .waitForFunction("!!window.WA_AUTHENTICATED || (document.getElementsByClassName('app')[0] && document.getElementsByClassName('app')[0].attributes && !!document.getElementsByClassName('app')[0].attributes.tabindex) || (document.getElementsByClassName('two')[0] && document.getElementsByClassName('two')[0].attributes && !!document.getElementsByClassName('two')[0].attributes.tabindex)", { timeout: 0 })
73
74
  .then(() => true));
74
75
  };
75
- exports.isInsideChat = isInsideChat;
76
+ const isTosBlocked = (waPage) => {
77
+ return (0, rxjs_1.from)(waPage
78
+ .waitForFunction(`document.getElementsByTagName("html")[0].classList[0] === 'no-js'`, { timeout: 0 })
79
+ .then(() => false));
80
+ };
76
81
  const waitForRipeSession = (waPage) => __awaiter(void 0, void 0, void 0, function* () {
77
82
  try {
78
83
  yield waPage.waitForFunction(`window.isRipeSession()`, { timeout: 0, polling: 'mutation' });
@@ -84,11 +89,17 @@ const waitForRipeSession = (waPage) => __awaiter(void 0, void 0, void 0, functio
84
89
  });
85
90
  exports.waitForRipeSession = waitForRipeSession;
86
91
  const sessionDataInvalid = (waPage) => __awaiter(void 0, void 0, void 0, function* () {
92
+ const check = `Object.keys(localStorage).includes("old-logout-cred")`;
87
93
  yield waPage
88
- .waitForFunction('!window.getQrPng', { timeout: 0, polling: 'mutation' });
89
- yield (0, browser_1.injectApi)(waPage);
90
- yield waPage
91
- .waitForFunction('!window.getQrPng', { timeout: 0, polling: 'mutation' });
94
+ .waitForFunction(check, { timeout: 0, polling: 'mutation' });
95
+ // await injectApi(waPage, null, true);
96
+ // await waPage
97
+ // .waitForFunction(
98
+ // '!window.getQrPng',
99
+ // { timeout: 0, polling: 'mutation' }
100
+ // )
101
+ // await timeout(1000000)
102
+ //NEED A DIFFERENT WAY TO DETERMINE IF THE SESSION WAS LOGGED OUT!!!!
92
103
  //if the code reaches here it means the browser was refreshed. Nuke the session data and restart `create`
93
104
  return 'NUKE';
94
105
  });
@@ -116,6 +127,7 @@ class QRManager {
116
127
  this.qrEvF(this.config);
117
128
  }
118
129
  qrEvF(config = this.config) {
130
+ return new events_1.EvEmitter(config.sessionId || 'session', 'qr');
119
131
  if (!this.qrEv)
120
132
  this.qrEv = new events_1.EvEmitter(config.sessionId || 'session', 'qr');
121
133
  return this.qrEv;
@@ -205,12 +217,13 @@ class QRManager {
205
217
  const fn = (qrData) => __awaiter(this, void 0, void 0, function* () {
206
218
  if (qrData.length > 200 && !(config === null || config === void 0 ? void 0 : config.multiDevice)) {
207
219
  spinner.fail(`Multi-Device detected, please set multiDevice to true in your config or add the --multi-device flag`);
220
+ spinner.emit(true, "MD_DETECT");
208
221
  return resolve(md);
209
222
  }
210
223
  if (!gotResult && (qrData === 'QR_CODE_SUCCESS' || qrData === md)) {
211
224
  gotResult = true;
212
225
  spinner === null || spinner === void 0 ? void 0 : spinner.succeed(qrData === md ? "Multi device support for this project is EXPERIMENTAL. Some things may not work...." : "QR code scanned. Loading session...");
213
- return resolve(yield (0, exports.isInsideChat)(waPage).toPromise());
226
+ return resolve(yield isInsideChat(waPage).toPromise());
214
227
  }
215
228
  if (!gotResult)
216
229
  this.grabAndEmit(qrData, waPage, config, spinner);
@@ -238,13 +251,17 @@ class QRManager {
238
251
  yield this.grabAndEmit(firstQr, waPage, config, spinner);
239
252
  });
240
253
  }
254
+ /**
255
+ * Wait 10 seconds for the qr element to show.
256
+ * If it doesn't show up within 10 seconds then assume the session is authed already or blocked therefore ignore and return promise
257
+ */
241
258
  waitFirstQr(waPage, config, spinner) {
242
259
  return __awaiter(this, void 0, void 0, function* () {
243
260
  const fqr = yield waPage.waitForFunction(`!!(${this.qrCheck})`, {
244
261
  polling: 500,
245
262
  timeout: 10000
246
263
  })
247
- .catch(e => false);
264
+ .catch(() => false);
248
265
  if (fqr)
249
266
  yield this.emitFirst(waPage, config, spinner);
250
267
  return;
@@ -45,12 +45,15 @@ const promise_1 = __importDefault(require("terminate/promise"));
45
45
  const logging_1 = require("../logging/logging");
46
46
  const tools_1 = require("../utils/tools");
47
47
  const auth_1 = require("./auth");
48
- let browser, wapiInjected = false, wapiAttempts = 1;
48
+ const script_preloader_1 = require("./script_preloader");
49
+ const patch_manager_1 = require("./patch_manager");
50
+ let browser, wapiInjected = false, dumbCache = undefined, wapiAttempts = 1;
49
51
  exports.BROWSER_START_TS = 0;
50
52
  function initPage(sessionId, config, customUserAgent, spinner, _page, skipAuth) {
51
53
  var _a, _b, _c, _d, _e;
52
54
  return __awaiter(this, void 0, void 0, function* () {
53
55
  const setupPromises = [];
56
+ script_preloader_1.scriptLoader.loadScripts();
54
57
  if ((config === null || config === void 0 ? void 0 : config.resizable) === undefined || !(config === null || config === void 0 ? void 0 : config.resizable) == false)
55
58
  config.defaultViewport = null;
56
59
  if (config === null || config === void 0 ? void 0 : config.useStealth) {
@@ -65,7 +68,28 @@ function initPage(sessionId, config, customUserAgent, spinner, _page, skipAuth)
65
68
  spinner === null || spinner === void 0 ? void 0 : spinner.info(`Browser launched: ${((0, tools_1.now)() - startBrowser).toFixed(0)}ms`);
66
69
  waPage = yield getWAPage(browser);
67
70
  }
71
+ //@ts-ignore
72
+ waPage._client.send('Network.setBypassServiceWorker', { bypass: true });
68
73
  const postBrowserLaunchTs = (0, tools_1.now)();
74
+ waPage.on("framenavigated", (frame) => __awaiter(this, void 0, void 0, function* () {
75
+ try {
76
+ const frameNavPromises = [];
77
+ const content = yield frame.content();
78
+ const webpPackKey = (((content.match(/self.(?:.*)=self.*\|\|\[\]/g) || [])[0] || "").match(/self.*\w?=/g) || [""])[0].replace("=", "").replace("self.", "") || false;
79
+ logging_1.log.info(`FRAME NAV, ${frame.url()}, ${webpPackKey}`);
80
+ if (webpPackKey) {
81
+ frameNavPromises.push(injectApi(waPage, spinner, true));
82
+ frameNavPromises.push(auth_1.qrManager.waitFirstQr(waPage, config, spinner));
83
+ }
84
+ if (frame.url().includes('post_logout=1')) {
85
+ console.log("Session most likely logged out");
86
+ }
87
+ yield Promise.all(frameNavPromises);
88
+ }
89
+ catch (error) {
90
+ logging_1.log.error('framenaverr', error);
91
+ }
92
+ }));
69
93
  spinner === null || spinner === void 0 ? void 0 : spinner.info('Setting Up Page');
70
94
  if (config === null || config === void 0 ? void 0 : config.proxyServerCredentials) {
71
95
  yield waPage.authenticate(config.proxyServerCredentials);
@@ -105,10 +129,29 @@ function initPage(sessionId, config, customUserAgent, spinner, _page, skipAuth)
105
129
  if (proxyAddr) {
106
130
  proxy = (yield Promise.resolve().then(() => __importStar(require('puppeteer-page-proxy')))).default;
107
131
  }
108
- if (interceptAuthentication || proxyAddr || blockCrashLogs) {
132
+ if (interceptAuthentication || proxyAddr || blockCrashLogs || true) {
109
133
  yield waPage.setRequestInterception(true);
134
+ waPage.on('response', (response) => __awaiter(this, void 0, void 0, function* () {
135
+ if (response.request().url() == "https://web.whatsapp.com/") {
136
+ const t = yield response.text();
137
+ if (t.includes(`class="no-js"`) && t.includes(`self.`) && !dumbCache) {
138
+ //this is a valid response, save it for later
139
+ dumbCache = t;
140
+ logging_1.log.info("saving valid page to dumb cache");
141
+ }
142
+ }
143
+ }));
110
144
  const authCompleteEv = new events_1.EvEmitter(sessionId, 'AUTH');
111
145
  waPage.on('request', (request) => __awaiter(this, void 0, void 0, function* () {
146
+ //local refresh cache:
147
+ if (request.url() === "https://web.whatsapp.com/" && dumbCache) {
148
+ //if the dumbCache isn't set and this response includes
149
+ logging_1.log.info("reviving page from dumb cache");
150
+ return yield request.respond({
151
+ status: 200,
152
+ body: dumbCache
153
+ });
154
+ }
112
155
  if (interceptAuthentication &&
113
156
  request.url().includes('_priority_components') &&
114
157
  !quickAuthed) {
@@ -184,7 +227,6 @@ function initPage(sessionId, config, customUserAgent, spinner, _page, skipAuth)
184
227
  //try twice
185
228
  const WEB_START_TS = new Date().getTime();
186
229
  const webRes = yield waPage.goto(puppeteer_config_1.puppeteerConfig.WAUrl);
187
- Promise.all([injectApi(waPage, spinner), auth_1.qrManager.waitFirstQr(waPage, config, spinner)]);
188
230
  const WEB_END_TS = new Date().getTime();
189
231
  if (webRes == null) {
190
232
  spinner === null || spinner === void 0 ? void 0 : spinner.info(`Page loaded but something may have gone wrong: ${WEB_END_TS - WEB_START_TS}ms`);
@@ -262,12 +304,15 @@ const getSessionDataFilePath = (sessionId, config) => {
262
304
  return false;
263
305
  };
264
306
  exports.getSessionDataFilePath = getSessionDataFilePath;
265
- const addScript = (page, js) => page.addScriptTag({
266
- path: require.resolve(path.join(__dirname, '../lib', js))
267
- });
307
+ const addScript = (page, js) => __awaiter(void 0, void 0, void 0, function* () { return page.evaluate(yield script_preloader_1.scriptLoader.getScript(js)); });
268
308
  exports.addScript = addScript;
309
+ // (page: Page, js : string) : Promise<unknown> => page.addScriptTag({
310
+ // path: require.resolve(path.join(__dirname, '../lib', js))
311
+ // })
269
312
  function injectPreApiScripts(page, spinner) {
270
313
  return __awaiter(this, void 0, void 0, function* () {
314
+ if (yield page.evaluate("!['jsSHA','axios', 'QRCode', 'Base64', 'objectHash'].find(x=>!window[x])"))
315
+ return;
271
316
  const t1 = yield (0, tools_1.timePromise)(() => Promise.all([
272
317
  'axios.min.js',
273
318
  'jsSha.min.js',
@@ -282,17 +327,33 @@ function injectPreApiScripts(page, spinner) {
282
327
  exports.injectPreApiScripts = injectPreApiScripts;
283
328
  function injectWapi(page, spinner, force = false) {
284
329
  return __awaiter(this, void 0, void 0, function* () {
330
+ const bruteInjectionAttempts = 1;
331
+ yield (0, patch_manager_1.earlyInjectionCheck)(page);
332
+ const check = `window.WAPI && window.Store ? true : false`;
333
+ const initCheck = yield page.evaluate(check);
334
+ if (initCheck)
335
+ return;
336
+ logging_1.log.info(`WAPI CHECK: ${initCheck}`);
337
+ if (!check)
338
+ force = true;
285
339
  if (wapiInjected && !force)
286
340
  return page;
287
- const check = `window.WAPI && window.Store ? true : false`;
288
- const wapi = yield (0, tools_1.timePromise)(() => (0, exports.addScript)(page, 'wapi.js'));
289
- spinner === null || spinner === void 0 ? void 0 : spinner.info(`WAPI inject: ${wapi}ms`);
341
+ const multiScriptInjectPromiseArr = Array(bruteInjectionAttempts).fill("wapi.js").map((_s) => (0, exports.addScript)(page, _s));
342
+ try {
343
+ const wapi = yield (0, tools_1.timePromise)(() => Promise.all(multiScriptInjectPromiseArr));
344
+ spinner === null || spinner === void 0 ? void 0 : spinner.info(`WAPI inject: ${wapi}ms`);
345
+ }
346
+ catch (error) {
347
+ logging_1.log.error("injectWapi ~ error", error.message);
348
+ //one of the injection attempts failed.
349
+ return yield injectWapi(page, spinner, force);
350
+ }
290
351
  spinner === null || spinner === void 0 ? void 0 : spinner.info("Checking session integrity");
291
352
  wapiAttempts++;
292
- wapiInjected = !!(yield page.waitForFunction(check, { timeout: 3000, polling: 200 }).catch(e => false));
353
+ wapiInjected = !!(yield page.waitForFunction(check, { timeout: 3000, polling: 50 }).catch(e => false));
293
354
  if (!wapiInjected) {
294
355
  spinner === null || spinner === void 0 ? void 0 : spinner.info(`Session integrity check failed, trying again... ${wapiAttempts}`);
295
- return yield injectWapi(page, spinner);
356
+ return yield injectWapi(page, spinner, true);
296
357
  }
297
358
  spinner === null || spinner === void 0 ? void 0 : spinner.info("Session integrity check passed");
298
359
  return page;
@@ -363,7 +363,7 @@ function create(config = {}) {
363
363
  waPage.on('error', error => {
364
364
  if (config === null || config === void 0 ? void 0 : config.logConsoleErrors)
365
365
  console.error(error);
366
- logging_1.log.error('Page Console Error:', error.text());
366
+ logging_1.log.error('Page Console Error:', error.message || (error === null || error === void 0 ? void 0 : error.text()));
367
367
  });
368
368
  if (config === null || config === void 0 ? void 0 : config.restartOnCrash)
369
369
  waPage.on('error', (error) => __awaiter(this, void 0, void 0, function* () {
@@ -401,6 +401,8 @@ function create(config = {}) {
401
401
  config.eventMode = true;
402
402
  const client = new Client_1.Client(waPage, config, debugInfo);
403
403
  const { me } = yield client.getMe();
404
+ const licIndex = process.argv.findIndex(arg => arg === "--license-key" || arg === "-l");
405
+ config.licenseKey = config.licenseKey || licIndex !== -1 && process.argv[licIndex + 1];
404
406
  if ((config === null || config === void 0 ? void 0 : config.licenseKey) || me._serialized !== earlyWid) {
405
407
  yield (0, patch_manager_1.getAndInjectLicense)(waPage, config, me, debugInfo, spinner, me._serialized !== earlyWid ? false : yield licensePromise);
406
408
  }
@@ -164,6 +164,8 @@ function getLicense(config, me, debugInfo, spinner) {
164
164
  exports.getLicense = getLicense;
165
165
  function earlyInjectionCheck(page) {
166
166
  return __awaiter(this, void 0, void 0, function* () {
167
+ //@ts-ignore
168
+ yield page.waitForFunction(() => Object.entries(window).filter(([, o]) => o && o.push && (o.push != [].push))[0] ? true : false, { timeout: 10, polling: 500 }).catch(() => { });
167
169
  //@ts-ignore
168
170
  return yield page.evaluate(() => { if (window.webpackChunkwhatsapp_web_client) {
169
171
  window.webpackChunkbuild = window.webpackChunkwhatsapp_web_client;
@@ -172,7 +174,7 @@ function earlyInjectionCheck(page) {
172
174
  (function () { const f = Object.entries(window).filter(([, o]) => o && o.push && (o.push != [].push)); if (f[0]) {
173
175
  window.webpackChunkbuild = window[f[0][0]];
174
176
  } })();
175
- } return (typeof webpackChunkbuild !== "undefined"); });
177
+ } return (typeof window.webpackChunkbuild !== "undefined"); });
176
178
  });
177
179
  }
178
180
  exports.earlyInjectionCheck = earlyInjectionCheck;
@@ -0,0 +1,17 @@
1
+ export declare class ScriptLoader {
2
+ scripts: string[];
3
+ contentRegistry: {
4
+ [key: string]: string;
5
+ };
6
+ constructor();
7
+ loadScripts(): Promise<{
8
+ [key: string]: string;
9
+ }>;
10
+ getScript(scriptName: string): Promise<string>;
11
+ flush(): void;
12
+ getScripts(): {
13
+ [key: string]: string;
14
+ };
15
+ }
16
+ declare const scriptLoader: ScriptLoader;
17
+ export { scriptLoader };