diff --git a/.env.example b/.env.example index 6cd9eb9..3add97c 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ -WWEBJS_TEST_SESSION_PATH=test_session.json -WWEBJS_TEST_REMOTE_ID=XXXXXXXXXX@c.us \ No newline at end of file +WWEBJS_TEST_REMOTE_ID=XXXXXXXXXX@c.us +WWEBJS_TEST_CLIENT_ID=authenticated +WWEBJS_TEST_MD=1 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 1e704a3..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -name: Bug report -about: Is something not working as intended? Report it here. -title: '' -assignees: '' - ---- - -### Bug description -A clear and concise description of what the bug is. - -### Reproduction steps -Steps to reproduce the behavior: -1. -2. -3. -... - -### Expected behavior -A clear and concise description of what you expected to happen. - -### Relevant code -If applicable, add code snippets to help explain your problem. - -### Environment (please complete the following information): -**WhatsApp** - - Account type [Standard / Business]: - - Device OS [iOS / Android]: - - WhatsApp Web version [run `await client.getWWebVersion()`]: - -**Library** - - Browser [Chrome / Chromium]: - - NodeJS version (`node -v`): - - npm or yarn version (`npm -v`): - - whatsapp-web.js version: - -**Other** - - Operating system (the one running node) [Linux / MacOS / Windows]: - - Operating system version (ex. Windows 10): -### Additional context -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..4fa8524 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,105 @@ +name: '🐛 Bug report' +description: Create a report to help us improve +labels: bug +body: + - type: markdown + attributes: + value: | + Thank you for reporting an issue :pray:. + + This issue tracker is for reporting bugs found in [`whatsapp-web.js`](https://github.com/pedroslopez/whatsapp-web.js). + + If you have a question about how to achieve something and are struggling, please post a question in our [Discord server](https://discord.gg/wyKybbF) instead. + + Before submitting a new bug/issue, please check the links below to see if there is a solution or question posted there already: + - `whatsapp-web.js` [Issues tab](https://github.com/pedroslopez/whatsapp-web.js/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) + - `whatsapp-web.js` [closed Issues tab](https://github.com/pedroslopez/whatsapp-web.js/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed) + + The more information you fill in, the better the community can help you. + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues + required: true + - type: textarea + id: description + attributes: + label: Describe the bug + description: Provide a clear and concise description of the challenge you are running into. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + description: Provide a clear and concise description of what you expected to happen. + placeholder: | + As a user, I expected ___ behavior but I am seeing ___ + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to Reproduce the Bug or Issue + description: Describe the steps we have to take to reproduce the behavior. + placeholder: | + 1. Do X + 2. Do Y + 3. Do Z + 4. See error + validations: + required: true + - type: textarea + id: relevant_code + attributes: + label: Relevant Code + description: If applicable, add code snippets to help explain your problem. + validations: + required: false + - type: dropdown + id: browser_type + attributes: + label: Browser Type + description: What web browser are you using? + options: + - Chromium + - Google Chrome + - Other (please write in Additional Context) + validations: + required: true + - type: dropdown + id: whatsapp_type + attributes: + label: WhatsApp Account Type + options: + - Standard + - WhatsApp Business + validations: + required: true + - type: dropdown + id: multidevice + attributes: + label: Does your WhatsApp account have multidevice enabled? + options: + - Yes, I am using Multi Device + - No, I am not using Multi Device + validations: + required: true + - type: textarea + attributes: + label: Environment + description: | + - OS: [e.g. Mac, Windows, Linux, Docker + Ubuntu 18, etc] + - Phone OS: [e.g. Android, iOS] + - whatsapp-web.js version [e.g. 1.2.3] + - WhatsApp Web version [run `await client.getWWebVersion()`]: + - Node.js Version [e.g. 1.2.3] + validations: + required: true + - type: textarea + id: additional + attributes: + label: Additional context + description: Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 11fc491..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..9f4bc70 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,42 @@ +name: 🚀 Feature request +description: Suggest an idea for this project +labels: enhancement + +body: + - type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue related to this feature request already exists. + options: + - label: I have searched the existing issues + required: true + + - type: textarea + attributes: + label: Is your feature request related to a problem? Please describe. + description: A concise description of the problem you are facing or the motivetion behind this feature request. + placeholder: I faced a problem due to ... + validations: + required: false + + - type: textarea + attributes: + label: Describe the solution you'd like. + description: A concise description of the solution for the issue. + validations: + required: true + + - type: textarea + attributes: + label: Describe an alternate solution. + description: Is there any other approach to solve the problem? + validations: + required: false + + - type: textarea + attributes: + label: Additional context + description: | + Links? Screenshots? References? Anything that will give us more context about what you would like to see! + validations: + required: false diff --git a/.gitignore b/.gitignore index f0b9c4f..79f6467 100644 --- a/.gitignore +++ b/.gitignore @@ -64,8 +64,10 @@ typings/ # next.js build output .next -# macOS Thumbnails +# macOS ._* +.DS_Store # Test sessions -*session.json \ No newline at end of file +*session.json +.wwebjs_auth/ \ No newline at end of file diff --git a/.npmignore b/.npmignore index c68d77a..a5131c9 100644 --- a/.npmignore +++ b/.npmignore @@ -12,6 +12,8 @@ yarn-debug.log* yarn-error.log* *session.json +.wwebjs_auth/ + .env tools/ tests/ diff --git a/README.md b/README.md index 7f76c99..03626ab 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![npm](https://img.shields.io/npm/v/whatsapp-web.js.svg)](https://www.npmjs.com/package/whatsapp-web.js) [![Depfu](https://badges.depfu.com/badges/4a65a0de96ece65fdf39e294e0c8dcba/overview.svg)](https://depfu.com/github/pedroslopez/whatsapp-web.js?project_id=9765) ![WhatsApp_Web 2.2202.12](https://img.shields.io/badge/WhatsApp_Web-2.2202.12-brightgreen.svg) [![Discord Chat](https://img.shields.io/discord/698610475432411196.svg?logo=discord)](https://discord.gg/H7DqQs4) +[![npm](https://img.shields.io/npm/v/whatsapp-web.js.svg)](https://www.npmjs.com/package/whatsapp-web.js) [![Depfu](https://badges.depfu.com/badges/4a65a0de96ece65fdf39e294e0c8dcba/overview.svg)](https://depfu.com/github/pedroslopez/whatsapp-web.js?project_id=9765) ![WhatsApp_Web 2.2220.8](https://img.shields.io/badge/WhatsApp_Web-2.2220.8-brightgreen.svg) [![Discord Chat](https://img.shields.io/discord/698610475432411196.svg?logo=discord)](https://discord.gg/H7DqQs4) # whatsapp-web.js A WhatsApp API client that connects through the WhatsApp Web browser app @@ -47,43 +47,14 @@ client.initialize(); Take a look at [example.js](https://github.com/pedroslopez/whatsapp-web.js/blob/master/example.js) for another example with more use cases. -## Remote Access +For more information on saving and restoring sessions, check out the available [Authentication Strategies](https://wwebjs.dev/guide/authentication.html). -You could also connect to any previously existing browser instance: - -```js -const client = new Client({ - puppeteer: { - browserWSEndpoint: `ws://localhost:3000` - } -}); -``` - -### Docker - -1) Installing a browser using browserless: - -``` -docker run \ - --rm \ - -p 3000:3000 \ - -e "MAX_CONCURRENT_SESSIONS=1" \ - browserless/chrome:latest -``` - -Reference: https://docs.browserless.io/docs/docker-quickstart.html - -### Remote Debugging - -2) Running a browser with websocket remote debugging enabled: -> chrome.exe --remote-debugging-port=9222 - -After that check the following webpage and check http://127.0.0.1:9220/json and get the **webSocketDebuggerUrl** ## Supported features | Feature | Status | | ------------- | ------------- | +| Multi Device | ✅ | | Send messages | ✅ | | Receive messages | ✅ | | Send media (images/audio/documents) | ✅ | @@ -116,11 +87,13 @@ Something missing? Make an issue and let us know! Pull requests are welcome! If you see something you'd like to add, please do. For drastic changes, please open an issue first. -## Donating +## Supporting the project -You can support the maintainer of this project through the link below +You can support the maintainer of this project through the links below -[![Support via PayPal](https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg)](https://www.paypal.me/psla/) +- [Support via GitHub Sponsors](https://github.com/sponsors/pedroslopez) +- [Support via PayPal](https://www.paypal.me/psla/) +- [Sign up for DigitalOcean](https://m.do.co/c/73f906a36ed4) and get $100 in credit when you sign up (Referral) ## Disclaimer diff --git a/docs/Base.html b/docs/Base.html index 431e533..f0946b9 100644 --- a/docs/Base.html +++ b/docs/Base.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Base + whatsapp-web.js 1.17.0 » Class: Base @@ -15,7 +15,7 @@ @@ -50,7 +50,7 @@ diff --git a/docs/BaseAuthStrategy.html b/docs/BaseAuthStrategy.html new file mode 100644 index 0000000..725c678 --- /dev/null +++ b/docs/BaseAuthStrategy.html @@ -0,0 +1,65 @@ + + + + + + + whatsapp-web.js 1.17.0 » Class: BaseAuthStrategy + + + + + + + + +
+
+
+
+ +
+
+
+

new BaseAuthStrategy()

+
+
+
+
+
+
+
+ +
+
+ + + + + + + + + + \ No newline at end of file diff --git a/docs/BusinessContact.html b/docs/BusinessContact.html index 32f1ef2..a027e9a 100644 --- a/docs/BusinessContact.html +++ b/docs/BusinessContact.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: BusinessContact + whatsapp-web.js 1.17.0 » Class: BusinessContact @@ -15,7 +15,7 @@ @@ -111,19 +111,22 @@
+
getCommonGroups()
+
+
getCountryCode()
getFormattedNumber()
-
getProfilePicUrl()
-
-
+
getProfilePicUrl()
+
+
unblock()
@@ -269,6 +272,15 @@
async
+

getCommonGroups() → Promise containing Array of WAWebJS.ChatId

+

Gets the Contact's common groups with you. Returns empty array if you don't have any common group.

+
+
Inherited from
+
Contact#getCommonGroups
+
Returns
+
+
+
async

getCountryCode() → Promise containing string

Returns the contact's countrycode, (1541859685@c.us) => (1)

@@ -314,7 +326,7 @@
diff --git a/docs/Buttons.html b/docs/Buttons.html index a6e6b29..95144f3 100644 --- a/docs/Buttons.html +++ b/docs/Buttons.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Buttons + whatsapp-web.js 1.17.0 » Class: Buttons @@ -15,7 +15,7 @@ @@ -234,7 +234,7 @@ Returns: [{ buttonId:'customId',buttonText:{'displayText':&#
diff --git a/docs/Call.html b/docs/Call.html index 5e027ba..0b8e543 100644 --- a/docs/Call.html +++ b/docs/Call.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Call + whatsapp-web.js 1.17.0 » Class: Call @@ -15,7 +15,7 @@ @@ -144,7 +144,7 @@
diff --git a/docs/Chat.html b/docs/Chat.html index ba808ca..9217ec2 100644 --- a/docs/Chat.html +++ b/docs/Chat.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Chat + whatsapp-web.js 1.17.0 » Class: Chat @@ -15,7 +15,7 @@ @@ -483,7 +483,7 @@
diff --git a/docs/Client.html b/docs/Client.html index e05aad7..1e6b66c 100644 --- a/docs/Client.html +++ b/docs/Client.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Client + whatsapp-web.js 1.17.0 » Class: Client @@ -15,7 +15,7 @@ @@ -26,7 +26,7 @@
async
+

getCommonGroups(contactId) → Promise containing Array of WAWebJS.ChatId

+

Gets the Contact's common groups with you. Returns empty array if you don't have any common group.

+
+

Parameter

+ + + + + + + + + + + + + + + + + +
NameTypeOptionalDescription
+

contactId

+
+

string

+
+

 

+
+

the whatsapp user's ID (_serialized format)

+
+
+
+
Returns
+
+

Promise containing Array of WAWebJS.ChatId 

+
+
+
async

getContactById(contactId) → Promise containing Contact

Get contact instance by ID

@@ -1452,6 +1429,11 @@
async
+

sendPresenceUnavailable()

+

Marks the client as unavailable

+
+
+
async

sendSeen(chatId) → Promise containing boolean

Mark as seen for the Chat

@@ -1491,7 +1473,7 @@
async
-

setDisplayName(displayName)

+

setDisplayName(displayName) → Promise containing Boolean

Sets the current user's display name. This is the name shown to WhatsApp users that have not added you as a contact beside your number in groups and in your profile.

@@ -1524,6 +1506,10 @@
+
Returns
+
+

Promise containing Boolean 

+
async

setStatus(status)

@@ -1650,104 +1636,10 @@

authenticated

Emitted when authentication is successful

-
-

Parameters

- - - - - - - - - - - - - - - - - -
NameTypeOptionalDescription
-

session

-
-

object

-
-

 

-
-

Object containing session information. Can be used to restore the session.

-

Values in session have the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeOptionalDescription
-

WABrowserId

-
-

string

-
-

 

-
-
-

WASecretBundle

-
-

string

-
-

 

-
-
-

WAToken1

-
-

string

-
-

 

-
-
-

WAToken2

-
-

string

-
-

 

-
-
-
-

change_battery

-

Emitted when the battery percentage for the attached device changes

+

Emitted when the battery percentage for the attached device changes. Will not be sent if using multi-device.

Parameters

@@ -1818,6 +1710,8 @@
+
Deprecated
+

change_state

Emitted when the connection state changes

@@ -2370,7 +2264,7 @@

qr

-

Emitted when the QR code is received

+

Emitted when a QR code is received

Parameter

@@ -2416,7 +2310,7 @@
diff --git a/docs/Client.js.html b/docs/Client.js.html index 5616fe9..763cd64 100644 --- a/docs/Client.js.html +++ b/docs/Client.js.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Source: Client.js + whatsapp-web.js 1.17.0 » Source: Client.js @@ -15,7 +15,7 @@ @@ -34,7 +34,6 @@ const EventEmitter = require('events'); const puppeteer = require('puppeteer'); const moduleRaid = require('@pedroslopez/moduleraid/moduleraid'); -const jsQR = require('jsqr'); const Util = require('./util/Util'); const InterfaceController = require('./util/InterfaceController'); @@ -42,22 +41,20 @@ const { WhatsWebURL, DefaultOptions, Events, WAState } = require('./ut const { ExposeStore, LoadUtils } = require('./util/Injected'); const ChatFactory = require('./factories/ChatFactory'); const ContactFactory = require('./factories/ContactFactory'); -const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification , Label, Call, Buttons, List} = require('./structures'); +const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification, Label, Call, Buttons, List} = require('./structures'); +const LegacySessionAuth = require('./authStrategies/LegacySessionAuth'); +const NoAuth = require('./authStrategies/NoAuth'); + /** * Starting point for interacting with the WhatsApp Web API * @extends {EventEmitter} * @param {object} options - Client options + * @param {AuthStrategy} options.authStrategy - Determines how to save and restore sessions. Will use LegacySessionAuth if options.session is set. Otherwise, NoAuth will be used. * @param {number} options.authTimeoutMs - Timeout for authentication selector in puppeteer * @param {object} options.puppeteer - Puppeteer launch options. View docs here: https://github.com/puppeteer/puppeteer/ - * @param {number} options.qrRefreshIntervalMs - Refresh interval for qr code (how much time to wait before checking if the qr code has changed) - * @param {number} options.qrTimeoutMs - Timeout for qr code selector in puppeteer * @param {number} options.qrMaxRetries - How many times should the qrcode be refreshed before giving up - * @param {string} options.restartOnAuthFail - Restart client with a new session (i.e. use null 'session' var) if authentication fails - * @param {object} options.session - Whatsapp session to restore. If not set, will start a new session - * @param {string} options.session.WABrowserId - * @param {string} options.session.WASecretBundle - * @param {string} options.session.WAToken1 - * @param {string} options.session.WAToken2 + * @param {string} options.restartOnAuthFail - @deprecated This option should be set directly on the LegacySessionAuth. + * @param {object} options.session - @deprecated Only here for backwards-compatibility. You should move to using LocalAuth, or set the authStrategy to LegacySessionAuth explicitly. * @param {number} options.takeoverOnConflict - If another whatsapp web session is detected (another browser), take over the session in the current browser * @param {number} options.takeoverTimeoutMs - How much time to wait before taking over the session * @param {string} options.userAgent - User agent to use in puppeteer @@ -79,13 +76,33 @@ const { ClientInfo, Message, MessageMedia, Contact, Location, GroupNotification * @fires Client#group_update * @fires Client#disconnected * @fires Client#change_state - * @fires Client#change_battery */ class Client extends EventEmitter { constructor(options = {}) { super(); this.options = Util.mergeDefault(DefaultOptions, options); + + if(!this.options.authStrategy) { + if(Object.prototype.hasOwnProperty.call(this.options, 'session')) { + process.emitWarning( + 'options.session is deprecated and will be removed in a future release due to incompatibility with multi-device. ' + + 'Use the LocalAuth authStrategy, don\'t pass in a session as an option, or suppress this warning by using the LegacySessionAuth strategy explicitly (see https://wwebjs.dev/guide/authentication.html#legacysessionauth-strategy).', + 'DeprecationWarning' + ); + + this.authStrategy = new LegacySessionAuth({ + session: this.options.session, + restartOnAuthFail: this.options.restartOnAuthFail + }); + } else { + this.authStrategy = new NoAuth(); + } + } else { + this.authStrategy = this.options.authStrategy; + } + + this.authStrategy.setup(this); this.pupBrowser = null; this.pupPage = null; @@ -98,41 +115,30 @@ class Client extends EventEmitter { */ async initialize() { let [browser, page] = [null, null]; - - if(this.options.puppeteer && this.options.puppeteer.browserWSEndpoint) { - browser = await puppeteer.connect(this.options.puppeteer); + + await this.authStrategy.beforeBrowserInitialized(); + + const puppeteerOpts = this.options.puppeteer; + if (puppeteerOpts && puppeteerOpts.browserWSEndpoint) { + browser = await puppeteer.connect(puppeteerOpts); page = await browser.newPage(); } else { - browser = await puppeteer.launch(this.options.puppeteer); + const browserArgs = [...(puppeteerOpts.args || [])]; + if(!browserArgs.find(arg => arg.includes('--user-agent'))) { + browserArgs.push(`--user-agent=${this.options.userAgent}`); + } + + browser = await puppeteer.launch({...puppeteerOpts, args: browserArgs}); page = (await browser.pages())[0]; } - + await page.setUserAgent(this.options.userAgent); + if (this.options.bypassCSP) await page.setBypassCSP(true); this.pupBrowser = browser; this.pupPage = page; - // remember me - await page.evaluateOnNewDocument(() => { - localStorage.setItem('remember-me', 'true'); - }); - - if (this.options.session) { - await page.evaluateOnNewDocument( - session => { - if(document.referrer === 'https://whatsapp.com/') { - localStorage.clear(); - localStorage.setItem('WABrowserId', session.WABrowserId); - localStorage.setItem('WASecretBundle', session.WASecretBundle); - localStorage.setItem('WAToken1', session.WAToken1); - localStorage.setItem('WAToken2', session.WAToken2); - } - }, this.options.session); - } - - if(this.options.bypassCSP) { - await page.setBypassCSP(true); - } + await this.authStrategy.afterBrowserInitialized(); await page.goto(WhatsWebURL, { waitUntil: 'load', @@ -140,56 +146,54 @@ class Client extends EventEmitter { referer: 'https://whatsapp.com/' }); - const KEEP_PHONE_CONNECTED_IMG_SELECTOR = '[data-icon="intro-md-beta-logo-dark"], [data-icon="intro-md-beta-logo-light"], [data-asset-intro-image-light="true"], [data-asset-intro-image-dark="true"]'; + const INTRO_IMG_SELECTOR = '[data-testid="intro-md-beta-logo-dark"], [data-testid="intro-md-beta-logo-light"], [data-asset-intro-image-light="true"], [data-asset-intro-image-dark="true"]'; + const INTRO_QRCODE_SELECTOR = 'div[data-ref] canvas'; - if (this.options.session) { - // Check if session restore was successful - try { - await page.waitForSelector(KEEP_PHONE_CONNECTED_IMG_SELECTOR, { timeout: this.options.authTimeoutMs }); - } catch (err) { - if (err.name === 'TimeoutError') { - /** - * Emitted when there has been an error while trying to restore an existing session - * @event Client#auth_failure - * @param {string} message - */ - this.emit(Events.AUTHENTICATION_FAILURE, 'Unable to log in. Are the session details valid?'); - browser.close(); - if (this.options.restartOnAuthFail) { - // session restore failed so try again but without session to force new authentication - this.options.session = null; - this.initialize(); - } - return; + // Checks which selector appears first + const needAuthentication = await Promise.race([ + new Promise(resolve => { + page.waitForSelector(INTRO_IMG_SELECTOR, { timeout: this.options.authTimeoutMs }) + .then(() => resolve(false)) + .catch((err) => resolve(err)); + }), + new Promise(resolve => { + page.waitForSelector(INTRO_QRCODE_SELECTOR, { timeout: this.options.authTimeoutMs }) + .then(() => resolve(true)) + .catch((err) => resolve(err)); + }) + ]); + + // Checks if an error ocurred on the first found selector. The second will be discarded and ignored by .race; + if (needAuthentication instanceof Error) throw needAuthentication; + + // Scan-qrcode selector was found. Needs authentication + if (needAuthentication) { + const { failed, failureEventPayload, restart } = await this.authStrategy.onAuthenticationNeeded(); + if(failed) { + /** + * Emitted when there has been an error while trying to restore an existing session + * @event Client#auth_failure + * @param {string} message + */ + this.emit(Events.AUTHENTICATION_FAILURE, failureEventPayload); + await this.destroy(); + if (restart) { + // session restore failed so try again but without session to force new authentication + return this.initialize(); } - - throw err; + return; } - } else { + const QR_CONTAINER = 'div[data-ref]'; + const QR_RETRY_BUTTON = 'div[data-ref] > span > button'; let qrRetries = 0; - - const getQrCode = async () => { - // Check if retry button is present - var QR_RETRY_SELECTOR = 'div[data-ref] > span > button'; - var qrRetry = await page.$(QR_RETRY_SELECTOR); - if (qrRetry) { - await qrRetry.click(); - } - - // Wait for QR Code - const QR_CANVAS_SELECTOR = 'canvas'; - await page.waitForSelector(QR_CANVAS_SELECTOR, { timeout: this.options.qrTimeoutMs }); - const qrImgData = await page.$eval(QR_CANVAS_SELECTOR, canvas => [].slice.call(canvas.getContext('2d').getImageData(0, 0, 264, 264).data)); - const qr = jsQR(qrImgData, 264, 264).data; - + await page.exposeFunction('qrChanged', async (qr) => { /** - * Emitted when the QR code is received + * 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) { @@ -197,15 +201,39 @@ class Client extends EventEmitter { await this.destroy(); } } - }; - getQrCode(); - this._qrRefreshInterval = setInterval(getQrCode, this.options.qrRefreshIntervalMs); + }); + + await page.evaluate(function (selectors) { + const qr_container = document.querySelector(selectors.QR_CONTAINER); + window.qrChanged(qr_container.dataset.ref); + + const obs = new MutationObserver((muts) => { + muts.forEach(mut => { + // Listens to qr token change + if (mut.type === 'attributes' && mut.attributeName === 'data-ref') { + window.qrChanged(mut.target.dataset.ref); + } else + // Listens to retry button, when found, click it + if (mut.type === 'childList') { + const retry_button = document.querySelector(selectors.QR_RETRY_BUTTON); + if (retry_button) retry_button.click(); + } + }); + }); + obs.observe(qr_container.parentElement, { + subtree: true, + childList: true, + attributes: true, + attributeFilter: ['data-ref'], + }); + }, { + QR_CONTAINER, + QR_RETRY_BUTTON + }); // Wait for code scan try { - await page.waitForSelector(KEEP_PHONE_CONNECTED_IMG_SELECTOR, { timeout: 0 }); - clearInterval(this._qrRefreshInterval); - this._qrRefreshInterval = undefined; + await page.waitForSelector(INTRO_IMG_SELECTOR, { timeout: 0 }); } catch(error) { if ( error.name === 'ProtocolError' && @@ -218,44 +246,29 @@ class Client extends EventEmitter { throw error; } + } await page.evaluate(ExposeStore, moduleRaid.toString()); - - // Get session tokens - const localStorage = JSON.parse(await page.evaluate(() => { - return JSON.stringify(window.localStorage); - })); - - const session = { - WABrowserId: localStorage.WABrowserId, - WASecretBundle: localStorage.WASecretBundle, - WAToken1: localStorage.WAToken1, - WAToken2: localStorage.WAToken2 - }; + const authEventPayload = await this.authStrategy.getAuthEventPayload(); /** * Emitted when authentication is successful * @event Client#authenticated - * @param {object} session Object containing session information. Can be used to restore the session. - * @param {string} session.WABrowserId - * @param {string} session.WASecretBundle - * @param {string} session.WAToken1 - * @param {string} session.WAToken2 */ - this.emit(Events.AUTHENTICATED, session); + this.emit(Events.AUTHENTICATED, authEventPayload); // Check window.Store Injection await page.waitForFunction('window.Store != undefined'); - const isMD = await page.evaluate(() => { - return window.Store.Features.features.MD_BACKEND; + await page.evaluate(async () => { + // safely unregister service workers + const registrations = await navigator.serviceWorker.getRegistrations(); + for (let registration of registrations) { + registration.unregister(); + } }); - if(isMD) { - throw new Error('Multi-device is not yet supported by whatsapp-web.js. Please check out https://github.com/pedroslopez/whatsapp-web.js/pull/889 to follow the progress.'); - } - //Load util functions (serializers, helper functions) await page.evaluate(LoadUtils); @@ -265,7 +278,7 @@ class Client extends EventEmitter { * @type {ClientInfo} */ this.info = new ClientInfo(this, await page.evaluate(() => { - return window.Store.Conn.serialize(); + return { ...window.Store.Conn.serialize(), wid: window.Store.User.getMeUser() }; })); // Add InterfaceController @@ -429,11 +442,12 @@ class Client extends EventEmitter { if (battery === undefined) return; /** - * Emitted when the battery percentage for the attached device changes + * Emitted when the battery percentage for the attached device changes. Will not be sent if using multi-device. * @event Client#change_battery * @param {object} batteryInfo * @param {number} batteryInfo.battery - The current battery percentage * @param {boolean} batteryInfo.plugged - Indicates if the phone is plugged in (true) or not (false) + * @deprecated */ this.emit(Events.BATTERY_CHANGED, { battery, plugged }); }); @@ -452,14 +466,14 @@ class Client extends EventEmitter { * @param {boolean} call.webClientShouldHandle - If Waweb should handle * @param {object} call.participants - Participants */ - const cll = new Call(this,call); + const cll = new Call(this, call); this.emit(Events.INCOMING_CALL, cll); }); - + await page.evaluate(() => { window.Store.Msg.on('change', (msg) => { window.onChangeMessageEvent(window.WWebJS.getMessageModel(msg)); }); window.Store.Msg.on('change:type', (msg) => { window.onChangeMessageTypeEvent(window.WWebJS.getMessageModel(msg)); }); - window.Store.Msg.on('change:ack', (msg,ack) => { window.onMessageAckEvent(window.WWebJS.getMessageModel(msg), ack); }); + window.Store.Msg.on('change:ack', (msg, ack) => { window.onMessageAckEvent(window.WWebJS.getMessageModel(msg), ack); }); window.Store.Msg.on('change:isUnsentMedia', (msg, unsent) => { if (msg.id.fromMe && !unsent) window.onMessageMediaUploadedEvent(window.WWebJS.getMessageModel(msg)); }); window.Store.Msg.on('remove', (msg) => { if (msg.isNewMsg) window.onRemoveMessageEvent(window.WWebJS.getMessageModel(msg)); }); window.Store.AppState.on('change:state', (_AppState, state) => { window.onAppStateChangedEvent(state); }); @@ -497,9 +511,6 @@ class Client extends EventEmitter { * Closes the client */ async destroy() { - if (this._qrRefreshInterval) { - clearInterval(this._qrRefreshInterval); - } await this.pupBrowser.close(); } @@ -507,9 +518,11 @@ class Client extends EventEmitter { * Logs out the client, closing the current session */ async logout() { - return await this.pupPage.evaluate(() => { + await this.pupPage.evaluate(() => { return window.Store.AppState.logout(); }); + + await this.authStrategy.logout(); } /** @@ -539,7 +552,7 @@ class Client extends EventEmitter { /** * Message options. * @typedef {Object} MessageSendOptions - * @property {boolean} [linkPreview=true] - Show links preview + * @property {boolean} [linkPreview=true] - Show links preview. Has no effect on multi-device accounts. * @property {boolean} [sendAudioAsVoice=false] - Send audio as voice message * @property {boolean} [sendVideoAsGif=false] - Send video as gif * @property {boolean} [sendMediaAsSticker=false] - Send media as a sticker @@ -589,34 +602,36 @@ class Client extends EventEmitter { } else if (content instanceof Location) { internalOptions.location = content; content = ''; - } else if(content instanceof Contact) { + } else if (content instanceof Contact) { internalOptions.contactCard = content.id._serialized; content = ''; - } else if(Array.isArray(content) && content.length > 0 && content[0] instanceof Contact) { + } else if (Array.isArray(content) && content.length > 0 && content[0] instanceof Contact) { internalOptions.contactCardList = content.map(contact => contact.id._serialized); content = ''; - } else if(content instanceof Buttons){ - if(content.type !== 'chat'){internalOptions.attachment = content.body;} + } else if (content instanceof Buttons) { + if (content.type !== 'chat') { internalOptions.attachment = content.body; } internalOptions.buttons = content; content = ''; - } else if(content instanceof List){ + } else if (content instanceof List) { internalOptions.list = content; content = ''; } - + if (internalOptions.sendMediaAsSticker && internalOptions.attachment) { - internalOptions.attachment = - await Util.formatToWebpSticker(internalOptions.attachment, { + internalOptions.attachment = await Util.formatToWebpSticker( + internalOptions.attachment, { name: options.stickerName, author: options.stickerAuthor, categories: options.stickerCategories - }); + }, this.pupPage + ); } const newMessage = await this.pupPage.evaluate(async (chatId, message, options, sendSeen) => { const chatWid = window.Store.WidFactory.createWid(chatId); const chat = await window.Store.Chat.find(chatWid); + if (sendSeen) { window.WWebJS.sendSeen(chatId); } @@ -703,7 +718,7 @@ class Client extends EventEmitter { */ async getInviteInfo(inviteCode) { return await this.pupPage.evaluate(inviteCode => { - return window.Store.Wap.groupInviteInfo(inviteCode); + return window.Store.InviteInfo.sendQueryGroupInvite(inviteCode); }, inviteCode); } @@ -722,25 +737,25 @@ class Client extends EventEmitter { /** * Accepts a private invitation to join a group - * @param {object} inviteV4 Invite V4 Info + * @param {object} inviteInfo Invite V4 Info * @returns {Promise<Object>} */ async acceptGroupV4Invite(inviteInfo) { - if(!inviteInfo.inviteCode) throw 'Invalid invite code, try passing the message.inviteV4 object'; + if (!inviteInfo.inviteCode) throw 'Invalid invite code, try passing the message.inviteV4 object'; if (inviteInfo.inviteCodeExp == 0) throw 'Expired invite code'; - return await this.pupPage.evaluate(async inviteInfo => { - let { groupId, fromId, inviteCode, inviteCodeExp, toId } = inviteInfo; - return await window.Store.Wap.acceptGroupV4Invite(groupId, fromId, inviteCode, String(inviteCodeExp), toId); + return this.pupPage.evaluate(async inviteInfo => { + let { groupId, fromId, inviteCode, inviteCodeExp } = inviteInfo; + return await window.Store.JoinInviteV4.sendJoinGroupViaInviteV4(inviteCode, String(inviteCodeExp), groupId, fromId); }, inviteInfo); } - + /** * Sets the current user's status message * @param {string} status New status message */ async setStatus(status) { await this.pupPage.evaluate(async status => { - return await window.Store.Wap.sendSetStatus(status); + return await window.Store.StatusUtils.setMyStatus(status); }, status); } @@ -748,13 +763,24 @@ class Client extends EventEmitter { * Sets the current user's display name. * This is the name shown to WhatsApp users that have not added you as a contact beside your number in groups and in your profile. * @param {string} displayName New display name + * @returns {Promise<Boolean>} */ async setDisplayName(displayName) { - await this.pupPage.evaluate(async displayName => { - return await window.Store.Wap.setPushname(displayName); - }, displayName); - } + const couldSet = await this.pupPage.evaluate(async displayName => { + if(!window.Store.Conn.canSetMyPushname()) return false; + if(window.Store.MDBackend) { + // TODO + return false; + } else { + const res = await window.Store.Wap.setPushname(displayName); + return !res.status || res.status === 200; + } + }, displayName); + + return couldSet; + } + /** * Gets the current connection state for the client * @returns {WAState} @@ -771,7 +797,16 @@ class Client extends EventEmitter { */ async sendPresenceAvailable() { return await this.pupPage.evaluate(() => { - return window.Store.Wap.sendPresenceAvailable(); + return window.Store.PresenceUtils.sendPresenceAvailable(); + }); + } + + /** + * Marks the client as unavailable + */ + async sendPresenceUnavailable() { + return await this.pupPage.evaluate(() => { + return window.Store.PresenceUtils.sendPresenceUnavailable(); }); } @@ -810,8 +845,9 @@ class Client extends EventEmitter { return true; } const MAX_PIN_COUNT = 3; - if (window.Store.Chat.models.length > MAX_PIN_COUNT) { - let maxPinned = window.Store.Chat.models[MAX_PIN_COUNT - 1].pin; + const chatModels = window.Store.Chat.getModelsArray(); + if (chatModels.length > MAX_PIN_COUNT) { + let maxPinned = chatModels[MAX_PIN_COUNT - 1].pin; if (maxPinned) { return false; } @@ -877,13 +913,43 @@ class Client extends EventEmitter { * @returns {Promise<string>} */ async getProfilePicUrl(contactId) { - const profilePic = await this.pupPage.evaluate((contactId) => { - return window.Store.Wap.profilePicFind(contactId); + const profilePic = await this.pupPage.evaluate(async contactId => { + try { + const chatWid = window.Store.WidFactory.createWid(contactId); + return await window.Store.ProfilePic.profilePicFind(chatWid); + } catch (err) { + if(err.name === 'ServerStatusCodeError') return undefined; + throw err; + } }, contactId); - + return profilePic ? profilePic.eurl : undefined; } + /** + * Gets the Contact's common groups with you. Returns empty array if you don't have any common group. + * @param {string} contactId the whatsapp user's ID (_serialized format) + * @returns {Promise<WAWebJS.ChatId[]>} + */ + async getCommonGroups(contactId) { + const commonGroups = await this.pupPage.evaluate(async (contactId) => { + const contact = window.Store.Contact.get(contactId); + if (contact.commonGroups) { + return contact.commonGroups.serialize(); + } + const status = await window.Store.findCommonGroups(contact); + if (status) { + return contact.commonGroups.serialize(); + } + return []; + }, contactId); + const chats = []; + for (const group of commonGroups) { + chats.push(group.id); + } + return chats; + } + /** * Force reset of connection state for the client */ @@ -899,10 +965,7 @@ class Client extends EventEmitter { * @returns {Promise<Boolean>} */ async isRegisteredUser(id) { - return await this.pupPage.evaluate(async (id) => { - let result = await window.Store.Wap.queryExist(id); - return result.jid !== undefined; - }, id); + return Boolean(await this.getNumberId(id)); } /** @@ -912,14 +975,15 @@ class Client extends EventEmitter { * @returns {Promise<Object|null>} */ async getNumberId(number) { - if (!number.endsWith('@c.us')) number += '@c.us'; - try { - return await this.pupPage.evaluate(async numberId => { - return window.WWebJS.getNumberId(numberId); - }, number); - } catch(_) { - return null; + if (!number.endsWith('@c.us')) { + number += '@c.us'; } + + return await this.pupPage.evaluate(async number => { + const result = await window.Store.QueryExist(number); + if (!result || result.wid === undefined) return null; + return result.wid; + }, number); } /** @@ -928,14 +992,14 @@ class Client extends EventEmitter { * @returns {Promise<string>} */ async getFormattedNumber(number) { - if(!number.endsWith('@s.whatsapp.net')) number = number.replace('c.us', 's.whatsapp.net'); - if(!number.includes('@s.whatsapp.net')) number = `${number}@s.whatsapp.net`; - + if (!number.endsWith('@s.whatsapp.net')) number = number.replace('c.us', 's.whatsapp.net'); + if (!number.includes('@s.whatsapp.net')) number = `${number}@s.whatsapp.net`; + return await this.pupPage.evaluate(async numberId => { return window.Store.NumberInfo.formattedPhoneNumber(numberId); }, number); } - + /** * Get the country code of a WhatsApp ID. * @param {string} number Number or ID @@ -948,7 +1012,7 @@ class Client extends EventEmitter { return window.Store.NumberInfo.findCC(numberId); }, number); } - + /** * Create a new group * @param {string} name group title @@ -967,12 +1031,9 @@ class Client extends EventEmitter { } const createRes = await this.pupPage.evaluate(async (name, participantIds) => { - const res = await window.Store.Wap.createGroup(name, participantIds); - console.log(res); - if (!res.status === 200) { - throw 'An error occurred while creating the group!'; - } - + const participantWIDs = participantIds.map(p => window.Store.WidFactory.createWid(p)); + const id = window.Store.MsgKey.newId(); + const res = await window.Store.GroupUtils.sendCreateGroup(name, participantWIDs, undefined, id); return res; }, name, participants); @@ -993,9 +1054,9 @@ class Client extends EventEmitter { async getLabels() { const labels = await this.pupPage.evaluate(async () => { return window.WWebJS.getLabels(); - }); + }); - return labels.map(data => new Label(this , data)); + return labels.map(data => new Label(this, data)); } /** @@ -1006,7 +1067,7 @@ class Client extends EventEmitter { async getLabelById(labelId) { const label = await this.pupPage.evaluate(async (labelId) => { return window.WWebJS.getLabel(labelId); - }, labelId); + }, labelId); return new Label(this, label); } @@ -1016,12 +1077,12 @@ class Client extends EventEmitter { * @param {string} chatId * @returns {Promise<Array<Label>>} */ - async getChatLabels(chatId){ + async getChatLabels(chatId) { const labels = await this.pupPage.evaluate(async (chatId) => { return window.WWebJS.getChatLabels(chatId); }, chatId); - return labels.map(data => new Label(this, data)); + return labels.map(data => new Label(this, data)); } /** @@ -1029,16 +1090,16 @@ class Client extends EventEmitter { * @param {string} labelId * @returns {Promise<Array<Chat>>} */ - async getChatsByLabelId(labelId){ + async getChatsByLabelId(labelId) { const chatIds = await this.pupPage.evaluate(async (labelId) => { const label = window.Store.Label.get(labelId); - const labelItems = label.labelItemCollection.models; + const labelItems = label.labelItemCollection.getModelsArray(); return labelItems.reduce((result, item) => { - if(item.parentType === 'Chat'){ + if (item.parentType === 'Chat') { result.push(item.parentId); } return result; - },[]); + }, []); }, labelId); return Promise.all(chatIds.map(id => this.getChatById(id))); @@ -1050,8 +1111,8 @@ class Client extends EventEmitter { */ async getBlockedContacts() { const blockedContacts = await this.pupPage.evaluate(() => { - let chatIds = window.Store.Blocklist.models.map(a => a.id._serialized); - return Promise.all(chatIds.map(id => window.WWebJS.getContact(id))); + let chatIds = window.Store.Blocklist.getModelsArray().map(a => a.id._serialized); + return Promise.all(chatIds.map(id => window.WWebJS.getContact(id))); }); return blockedContacts.map(contact => ContactFactory.create(this.client, contact)); @@ -1069,7 +1130,7 @@ module.exports = Client;
diff --git a/docs/ClientInfo.html b/docs/ClientInfo.html index 1ea8a5e..dc67b2d 100644 --- a/docs/ClientInfo.html +++ b/docs/ClientInfo.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: ClientInfo + whatsapp-web.js 1.17.0 » Class: ClientInfo @@ -15,7 +15,7 @@ @@ -101,7 +101,7 @@

phone  object

-

Information about the phone this client is connected to

+

Information about the phone this client is connected to. Not available in multi-device.

Properties

@@ -188,10 +188,12 @@
+
Deprecated
+

platform  string

-

Platform the phone is running on

+

Platform WhatsApp is running on

pushname @@ -211,6 +213,8 @@

getBatteryStatus() → (object, number, or boolean)

Get current battery percentage and charging status for the attached device

+
Deprecated
+
Returns

object  @@ -238,7 +242,7 @@

diff --git a/docs/Contact.html b/docs/Contact.html index e5d77bc..eee5ec9 100644 --- a/docs/Contact.html +++ b/docs/Contact.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Contact + whatsapp-web.js 1.17.0 » Class: Contact @@ -15,7 +15,7 @@ @@ -108,19 +108,22 @@

+
getCommonGroups()
+
+
getCountryCode()
getFormattedNumber()
-
getProfilePicUrl()
-
-
+
getProfilePicUrl()
+
+
unblock()
@@ -236,6 +239,15 @@
async
+

getCommonGroups() → Promise containing Array of WAWebJS.ChatId

+

Gets the Contact's common groups with you. Returns empty array if you don't have any common group.

+
+
Returns
+
+

Promise containing Array of WAWebJS.ChatId 

+
+
+
async

getCountryCode() → Promise containing string

Returns the contact's countrycode, (1541859685@c.us) => (1)

@@ -281,7 +293,7 @@
diff --git a/docs/GroupChat.html b/docs/GroupChat.html index b697aca..d996d77 100644 --- a/docs/GroupChat.html +++ b/docs/GroupChat.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: GroupChat + whatsapp-web.js 1.17.0 » Class: GroupChat @@ -15,7 +15,7 @@ @@ -645,12 +645,14 @@
async
-

revokeInvite() → Promise

+

revokeInvite() → Promise containing string

Invalidates the current group invite code and generates a new one

Returns
-

Promise 

+

Promise containing string  +

New invite code

+

async
@@ -731,7 +733,7 @@
Chat#sendStateTyping
async
-

setDescription(description) → Promise

+

setDescription(description) → Promise containing boolean

Updates the group description

Parameter

@@ -764,7 +766,9 @@
Returns
-

Promise 

+

Promise containing boolean  +

Returns true if the description was properly updated. This can return false if the user does not have the necessary permissions.

+

async
@@ -850,7 +854,7 @@
async
-

setSubject(subject) → Promise

+

setSubject(subject) → Promise containing boolean

Updates the group subject

Parameter

@@ -883,7 +887,9 @@
Returns
-

Promise 

+

Promise containing boolean  +

Returns true if the subject was properly updated. This can return false if the user does not have the necessary permissions.

+

async
@@ -921,7 +927,7 @@
diff --git a/docs/GroupNotification.html b/docs/GroupNotification.html index f446b0b..69feb31 100644 --- a/docs/GroupNotification.html +++ b/docs/GroupNotification.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: GroupNotification + whatsapp-web.js 1.17.0 » Class: GroupNotification @@ -15,7 +15,7 @@ @@ -233,7 +233,7 @@
diff --git a/docs/InterfaceController.html b/docs/InterfaceController.html index d351316..0609cac 100644 --- a/docs/InterfaceController.html +++ b/docs/InterfaceController.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: InterfaceController + whatsapp-web.js 1.17.0 » Class: InterfaceController @@ -15,7 +15,7 @@ @@ -382,7 +382,7 @@
diff --git a/docs/Label.html b/docs/Label.html index fa8a910..950b6a5 100644 --- a/docs/Label.html +++ b/docs/Label.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Label + whatsapp-web.js 1.17.0 » Class: Label @@ -15,7 +15,7 @@ @@ -163,7 +163,7 @@
diff --git a/docs/LegacySessionAuth.html b/docs/LegacySessionAuth.html new file mode 100644 index 0000000..838a285 --- /dev/null +++ b/docs/LegacySessionAuth.html @@ -0,0 +1,188 @@ + + + + + + + whatsapp-web.js 1.17.0 » Class: LegacySessionAuth + + + + + + + + +
+
+
+
+ +
+
+
+

new LegacySessionAuth(options)

+
+

Parameters

+ + + + + + + + + + + + + + + + + +
NameTypeOptionalDescription
+

options

+
+

 

+
+

 

+
+

options

+

Values in options have the following properties:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeOptionalDescription
+

restartOnAuthFail

+
+

 

+
+

 

+
+

Restart client with a new session (i.e. use null 'session' var) if authentication fails

+
+

session

+
+

 

+
+

 

+
+

Whatsapp session to restore. If not set, will start a new session

+
+

session.WABrowserId

+
+

 

+
+

 

+
+
+

session.WASecretBundle

+
+

 

+
+

 

+
+
+

session.WAToken1

+
+

 

+
+

 

+
+
+

session.WAToken2

+
+

 

+
+

 

+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/docs/List.html b/docs/List.html index 1d8156c..13e0087 100644 --- a/docs/List.html +++ b/docs/List.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: List + whatsapp-web.js 1.17.0 » Class: List @@ -15,7 +15,7 @@ @@ -256,7 +256,7 @@ Returns: [{'title':'sectionTitle','rows':[{'r
diff --git a/docs/LocalAuth.html b/docs/LocalAuth.html new file mode 100644 index 0000000..5132be5 --- /dev/null +++ b/docs/LocalAuth.html @@ -0,0 +1,135 @@ + + + + + + + whatsapp-web.js 1.17.0 » Class: LocalAuth + + + + + + + + +
+
+
+
+ +
+
+
+

new LocalAuth(options)

+
+

Parameters

+ + + + + + + + + + + + + + + + + +
NameTypeOptionalDescription
+

options

+
+

 

+
+

 

+
+

options

+

Values in options have the following properties:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeOptionalDescription
+

clientId

+
+

 

+
+

 

+
+

Client id to distinguish instances if you are using multiple, otherwise keep null if you are using only one instance

+
+

dataPath

+
+

 

+
+

 

+
+

Change the default path for saving session files, default is: "./.wwebjs_auth/"

+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/docs/Location.html b/docs/Location.html index 6a672e8..fe356b5 100644 --- a/docs/Location.html +++ b/docs/Location.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Location + whatsapp-web.js 1.17.0 » Class: Location @@ -15,7 +15,7 @@ @@ -149,7 +149,7 @@
diff --git a/docs/Message.html b/docs/Message.html index d9b2f59..46a641f 100644 --- a/docs/Message.html +++ b/docs/Message.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Message + whatsapp-web.js 1.17.0 » Class: Message @@ -15,7 +15,7 @@ @@ -54,6 +54,9 @@
deviceType
+
duration
+
+
forwardingScore
@@ -97,13 +100,13 @@
links
+
location
+
+
-
location
-
-
mediaKey
@@ -113,6 +116,9 @@
orderId
+
rawData
+
+
timestamp
@@ -152,13 +158,13 @@
getChat()
+
getContact()
+
+
-
getContact()
-
-
getInfo()
@@ -171,11 +177,17 @@
getPayment()
+
getQuotedMessage()
+
+
+
react(reaction)
+
+
-
getQuotedMessage()
+
reload()
reply(content[, chatId][, options])
@@ -227,6 +239,11 @@

String that represents from which device type the message was sent

+

duration +  string

+

Indicates the duration of the message in seconds

+
+

forwardingScore  number

Indicates how many times the message was forwarded.

@@ -313,6 +330,11 @@

Order ID for message type ORDER

+

rawData +  Object

+

Returns message in a raw format

+
+

timestamp  number

Unix timestamp for when the message was created

@@ -496,6 +518,54 @@
async
+

react(reaction) → Promise

+

React to this message with an emoji

+
+

Parameter

+ + + + + + + + + + + + + + + + + +
NameTypeOptionalDescription
+

reaction

+
+

string

+
+

 

+
+

Emoji to react with. Send an empty string to remove the reaction.

+
+
+
+
Returns
+
+

Promise 

+
+
+
async
+

reload() → Promise containing Message

+

Reloads this Message object's data in-place with the latest values from WhatsApp Web. + Note that the Message must still be in the web app cache for this to work, otherwise will return null.

+
+
Returns
+
+

Promise containing Message 

+
+
+
async

reply(content[, chatId][, options]) → Promise containing Message

Sends a message as a reply to this message. If chatId is specified, it will be sent through the specified Chat. If not, it will send the message @@ -580,7 +650,7 @@

diff --git a/docs/MessageMedia.html b/docs/MessageMedia.html index 0442fd7..0ba5c86 100644 --- a/docs/MessageMedia.html +++ b/docs/MessageMedia.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: MessageMedia + whatsapp-web.js 1.17.0 » Class: MessageMedia @@ -15,7 +15,7 @@ @@ -343,7 +343,7 @@
diff --git a/docs/NoAuth.html b/docs/NoAuth.html new file mode 100644 index 0000000..e875606 --- /dev/null +++ b/docs/NoAuth.html @@ -0,0 +1,66 @@ + + + + + + + whatsapp-web.js 1.17.0 » Class: NoAuth + + + + + + + + +
+
+
+
+ +
+
+
+

new NoAuth()

+
+
+
+
+
+
+
+ +
+
+
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/docs/Order.html b/docs/Order.html index 39a3668..830b862 100644 --- a/docs/Order.html +++ b/docs/Order.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Order + whatsapp-web.js 1.17.0 » Class: Order @@ -15,7 +15,7 @@ @@ -102,7 +102,7 @@
diff --git a/docs/PrivateChat.html b/docs/PrivateChat.html index 0990185..1ee0389 100644 --- a/docs/PrivateChat.html +++ b/docs/PrivateChat.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: PrivateChat + whatsapp-web.js 1.17.0 » Class: PrivateChat @@ -15,7 +15,7 @@ @@ -519,7 +519,7 @@
diff --git a/docs/PrivateContact.html b/docs/PrivateContact.html index 388084a..a8f1e87 100644 --- a/docs/PrivateContact.html +++ b/docs/PrivateContact.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: PrivateContact + whatsapp-web.js 1.17.0 » Class: PrivateContact @@ -15,7 +15,7 @@ @@ -108,19 +108,22 @@
+
getCommonGroups()
+
+
getCountryCode()
getFormattedNumber()
-
getProfilePicUrl()
-
-
+
getProfilePicUrl()
+
+
unblock()
@@ -262,6 +265,15 @@
async
+

getCommonGroups() → Promise containing Array of WAWebJS.ChatId

+

Gets the Contact's common groups with you. Returns empty array if you don't have any common group.

+
+
Inherited from
+
Contact#getCommonGroups
+
Returns
+
+
+
async

getCountryCode() → Promise containing string

Returns the contact's countrycode, (1541859685@c.us) => (1)

@@ -307,7 +319,7 @@
diff --git a/docs/Product.html b/docs/Product.html index 6359b7a..ae464cb 100644 --- a/docs/Product.html +++ b/docs/Product.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Product + whatsapp-web.js 1.17.0 » Class: Product @@ -15,7 +15,7 @@ @@ -127,7 +127,7 @@
diff --git a/docs/Util.html b/docs/Util.html index 696d76d..18dbc00 100644 --- a/docs/Util.html +++ b/docs/Util.html @@ -4,7 +4,7 @@ - whatsapp-web.js 1.15.6 » Class: Util + whatsapp-web.js 1.17.0 » Class: Util @@ -15,7 +15,7 @@ @@ -26,7 +26,7 @@