"use strict"; /* * This file is part of WPPConnect. * * WPPConnect is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * WPPConnect is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with WPPConnect. If not, see . */ Object.defineProperty(exports, "__esModule", { value: true }); exports.HostLayer = void 0; const create_config_1 = require("../../config/create-config"); const auth_1 = require("../../controllers/auth"); const browser_1 = require("../../controllers/browser"); const logger_1 = require("../../utils/logger"); const sleep_1 = require("../../utils/sleep"); const helpers_1 = require("../helpers"); class HostLayer { constructor(page, session, options) { this.page = page; this.autoCloseInterval = null; this.autoCloseCalled = false; this.isInitialized = false; this.isInjected = false; this.isStarted = false; this.isLogged = false; this.isInChat = false; this.checkStartInterval = null; this.urlCode = ''; this.attempt = 0; this.catchQR = null; this.statusFind = null; this.onLoadingScreen = null; this.catchLinkCode = null; this.session = session; this.options = { ...create_config_1.defaultOptions, ...options }; this.logger = this.options.logger || logger_1.defaultLogger; this.log('info', 'Initializing...'); this.initialize(); } log(level, message, meta = {}) { this.logger.log({ level, message, session: this.session, type: 'client', ...meta, }); } async initialize() { this.page.on('close', () => { this.cancelAutoClose(); this.log('verbose', 'Page Closed', { type: 'page' }); }); this.page.on('load', () => { this.log('verbose', 'Page loaded', { type: 'page' }); this.afterPageLoad(); }); this.isInitialized = true; } async afterPageLoad() { this.log('verbose', 'Injecting wapi.js'); const options = { deviceName: this.options.deviceName, disableGoogleAnalytics: this.options.disableGoogleAnalytics, googleAnalyticsId: this.options.googleAnalyticsId, linkPreviewApiServers: this.options.linkPreviewApiServers, poweredBy: this.options.poweredBy, }; await (0, helpers_1.evaluateAndReturn)(this.page, (options) => { window.WPPConfig = options; }, options); this.isInjected = false; await (0, browser_1.injectApi)(this.page, this.onLoadingScreen) .then(() => { this.isInjected = true; this.log('verbose', 'wapi.js injected'); this.afterPageScriptInjected(); }) .catch((e) => { console.log(e); this.log('verbose', 'wapi.js failed'); this.log('error', e); }); } async afterPageScriptInjected() { this.getWAVersion() .then((version) => { this.log('info', `WhatsApp WEB version: ${version}`); }) .catch(() => null); this.getWAJSVersion() .then((version) => { this.log('info', `WA-JS version: ${version}`); }) .catch(() => null); (0, helpers_1.evaluateAndReturn)(this.page, () => { WPP.on('conn.auth_code_change', window.checkQrCode); }).catch(() => null); (0, helpers_1.evaluateAndReturn)(this.page, () => { WPP.on('conn.main_ready', window.checkInChat); }).catch(() => null); this.checkInChat(); this.checkQrCode(); } async start() { if (this.isStarted) { return; } this.isStarted = true; await (0, browser_1.initWhatsapp)(this.page, null, false, this.options.whatsappVersion, this.options.proxy, this.log.bind(this)); await this.page.exposeFunction('checkQrCode', () => this.checkQrCode()); /*await this.page.exposeFunction('loginByCode', (phone: string) => this.loginByCode(phone) );*/ await this.page.exposeFunction('checkInChat', () => this.checkInChat()); this.checkStartInterval = setInterval(() => this.checkStart(), 5000); this.page.on('close', () => { clearInterval(this.checkStartInterval); }); } async checkStart() { (0, auth_1.needsToScan)(this.page) .then((need) => { }) .catch(() => null); } async checkQrCode() { var _a; const needScan = await (0, auth_1.needsToScan)(this.page).catch(() => null); this.isLogged = !needScan; if (!needScan) { this.attempt = 0; return; } const result = await this.getQrCode(); if (!(result === null || result === void 0 ? void 0 : result.urlCode) || this.urlCode === result.urlCode) { return; } if (typeof this.options.phoneNumber === 'string') { return this.loginByCode(this.options.phoneNumber); } this.urlCode = result.urlCode; this.attempt++; let qr = ''; if (this.options.logQR || this.catchQR) { qr = await (0, auth_1.asciiQr)(this.urlCode); } if (this.options.logQR) { this.log('info', `Waiting for QRCode Scan (Attempt ${this.attempt})...:\n${qr}`, { code: this.urlCode }); } else { this.log('verbose', `Waiting for QRCode Scan: Attempt ${this.attempt}`); } (_a = this.catchQR) === null || _a === void 0 ? void 0 : _a.call(this, result.base64Image, qr, this.attempt, result.urlCode); } async loginByCode(phone) { var _a; const code = await (0, helpers_1.evaluateAndReturn)(this.page, async ({ phone }) => { return JSON.parse(JSON.stringify(await WPP.conn.genLinkDeviceCodeForPhoneNumber(phone))); }, { phone }); if (this.options.logQR) { this.log('info', `Waiting for Login By Code (Code: ${code})\n`); } else { this.log('verbose', `Waiting for Login By Code`); } (_a = this.catchLinkCode) === null || _a === void 0 ? void 0 : _a.call(this, code); } async checkInChat() { var _a; const inChat = await (0, auth_1.isInsideChat)(this.page).catch(() => null); this.isInChat = !!inChat; if (!inChat) { return; } this.log('http', 'Connected'); (_a = this.statusFind) === null || _a === void 0 ? void 0 : _a.call(this, 'inChat', this.session); } tryAutoClose() { if (this.autoCloseInterval) { this.cancelAutoClose(); } if ((this.options.autoClose > 0 || this.options.deviceSyncTimeout > 0) && !this.autoCloseInterval && !this.page.isClosed()) { this.log('info', 'Closing the page'); this.autoCloseCalled = true; this.statusFind && this.statusFind('autocloseCalled', this.session); try { this.page.close(); } catch (error) { } } } startAutoClose(time = null) { if (time === null || time === undefined) { time = this.options.autoClose; } if (time > 0 && !this.autoCloseInterval) { const seconds = Math.round(time / 1000); this.log('info', `Auto close configured to ${seconds}s`); let remain = seconds; this.autoCloseInterval = setInterval(() => { if (this.page.isClosed()) { this.cancelAutoClose(); return; } remain -= 1; if (remain % 10 === 0 || remain <= 5) { this.log('http', `Auto close remain: ${remain}s`); } if (remain <= 0) { this.tryAutoClose(); } }, 1000); } } cancelAutoClose() { clearInterval(this.autoCloseInterval); this.autoCloseInterval = null; } async getQrCode() { let qrResult; qrResult = await (0, helpers_1.scrapeImg)(this.page).catch(() => undefined); return qrResult; } async waitForQrCodeScan() { if (!this.isStarted) { throw new Error('waitForQrCodeScan error: Session not started'); } while (!this.page.isClosed() && !this.isLogged) { await (0, sleep_1.sleep)(200); const needScan = await (0, auth_1.needsToScan)(this.page).catch(() => null); this.isLogged = !needScan; } } async waitForInChat() { if (!this.isStarted) { throw new Error('waitForInChat error: Session not started'); } if (!this.isLogged) { return false; } const start = Date.now(); while (!this.page.isClosed() && this.isLogged && !this.isInChat) { if (this.options.deviceSyncTimeout > 0 && Date.now() - start >= this.options.deviceSyncTimeout) { return false; } await (0, sleep_1.sleep)(1000); const inChat = (0, auth_1.isInsideChat)(this.page).catch(() => null); this.isInChat = !!inChat; } return this.isInChat; } async waitForPageLoad() { while (!this.isInjected) { await (0, sleep_1.sleep)(200); } await this.page.waitForFunction(() => WPP.isReady).catch(() => { }); } async waitForLogin() { var _a, _b, _c, _d, _e, _f; this.log('http', 'Waiting page load'); await this.waitForPageLoad(); this.log('http', 'Checking is logged...'); let authenticated = await (0, auth_1.isAuthenticated)(this.page).catch(() => null); this.startAutoClose(); if (authenticated === false) { this.log('http', typeof this.options.phoneNumber === 'string' ? 'Waiting for Login by Code...' : 'Waiting for QRCode Scan...'); (_a = this.statusFind) === null || _a === void 0 ? void 0 : _a.call(this, 'notLogged', this.session); await this.waitForQrCodeScan(); this.log('http', typeof this.options.phoneNumber === 'string' ? 'Checking Login by Code status...' : 'Checking QRCode status...'); // Wait for interface update await (0, sleep_1.sleep)(200); authenticated = await (0, auth_1.isAuthenticated)(this.page).catch(() => null); if (authenticated === null) { this.log('warn', 'Failed to authenticate'); (_b = this.statusFind) === null || _b === void 0 ? void 0 : _b.call(this, 'qrReadError', this.session); } else if (authenticated) { this.log('http', 'Login with success'); (_c = this.statusFind) === null || _c === void 0 ? void 0 : _c.call(this, 'qrReadSuccess', this.session); } else { this.log('warn', 'Login Fail'); (_d = this.statusFind) === null || _d === void 0 ? void 0 : _d.call(this, 'qrReadFail', this.session); this.tryAutoClose(); throw new Error('Failed to read the QRCode'); } } else if (authenticated === true) { this.log('http', 'Authenticated'); (_e = this.statusFind) === null || _e === void 0 ? void 0 : _e.call(this, 'isLogged', this.session); } if (authenticated === true) { // Reinicia o contador do autoclose this.cancelAutoClose(); // Wait for interface update await (0, sleep_1.sleep)(200); this.startAutoClose(this.options.deviceSyncTimeout); this.log('http', 'Checking phone is connected...'); const inChat = await this.waitForInChat(); if (!inChat) { this.log('warn', 'Phone not connected'); (_f = this.statusFind) === null || _f === void 0 ? void 0 : _f.call(this, 'phoneNotConnected', this.session); this.tryAutoClose(); throw new Error('Phone not connected'); } this.cancelAutoClose(); return true; } if (authenticated === false) { this.tryAutoClose(); this.log('warn', 'Not logged'); throw new Error('Not logged'); } this.tryAutoClose(); if (this.autoCloseCalled) { this.log('error', 'Auto Close Called'); throw new Error('Auto Close Called'); } if (this.page.isClosed()) { this.log('error', 'Page Closed'); throw new Error('Page Closed'); } this.log('error', 'Unknow error'); throw new Error('Unknow error'); } /** * @category Host * @returns Current host device details */ async getHostDevice() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WAPI.getHost()); } /** * @category Host * @returns Current wid connected */ async getWid() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WAPI.getWid()); } /** * Retrieves WA version * @category Host */ async getWAVersion() { await this.page .waitForFunction(() => WAPI.getWAVersion()) .catch(() => null); return await (0, helpers_1.evaluateAndReturn)(this.page, () => WAPI.getWAVersion()); } /** * Retrieves WA-JS version * @category Host */ async getWAJSVersion() { await this.page.waitForFunction(() => WPP.version).catch(() => null); return await (0, helpers_1.evaluateAndReturn)(this.page, () => WPP.version); } /** * Retrieves the connection state * @category Host */ async getConnectionState() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => { return WPP.whatsapp.Socket.state; }); } /** * Retrieves if the phone is online. Please note that this may not be real time. * @category Host */ async isConnected() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WAPI.isConnected()); } /** * Check is online * @category Host */ async isOnline() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WPP.conn.isOnline()); } /** * Retrieves if the phone is online. Please note that this may not be real time. * @category Host */ async isLoggedIn() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WAPI.isLoggedIn()); } /** * Retrieves Battery Level * @category Host */ async getBatteryLevel() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WAPI.getBatteryLevel()); } /** * Start phone Watchdog, forcing the phone connection verification. * * @category Host * @param interval interval number in miliseconds */ async startPhoneWatchdog(interval = 15000) { return await (0, helpers_1.evaluateAndReturn)(this.page, (interval) => WAPI.startPhoneWatchdog(interval), interval); } /** * Stop phone Watchdog, more details in {@link startPhoneWatchdog} * @category Host */ async stopPhoneWatchdog(interval) { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WAPI.stopPhoneWatchdog()); } /** * Check the current session is an MultiDevice session * @category Host */ async isMultiDevice() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WPP.conn.isMultiDevice()); } /** * Retrieve main interface is authenticated, loaded and synced * @category Host */ async isMainReady() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WPP.conn.isMainReady()); } /** * Retrieve if is authenticated * @category Host */ async isAuthenticated() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WPP.conn.isAuthenticated()); } /** * Retrieve if main interface is authenticated and loaded, bot not synced * @category Host */ async isMainLoaded() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WPP.conn.isMainLoaded()); } /** * Retrieve if main interface is initializing * @category Host */ async isMainInit() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WPP.conn.isMainInit()); } /** * Join or leave of WhatsApp Web beta program. * Will return the value seted * @category Host */ async joinWebBeta(value) { return await (0, helpers_1.evaluateAndReturn)(this.page, (value) => WPP.conn.joinWebBeta(value), value); } /** * Get WhatsApp build constants * @category Host * @returns Build constants information */ async getBuildConstants() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WPP.conn.getBuildConstants()); } /** * Check if the account has been migrated to LID * @category Host * @returns true if the account has been migrated to LID, false otherwise */ async isLidMigrated() { return await (0, helpers_1.evaluateAndReturn)(this.page, () => WPP.whatsapp.functions.isLidMigrated()); } } exports.HostLayer = HostLayer;