whatsapp-web.js 1.15.8 → 1.16.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/.env.example CHANGED
@@ -1,2 +1,3 @@
1
- WWEBJS_TEST_SESSION_PATH=test_session.json
2
- WWEBJS_TEST_REMOTE_ID=XXXXXXXXXX@c.us
1
+ WWEBJS_TEST_REMOTE_ID=XXXXXXXXXX@c.us
2
+ WWEBJS_TEST_CLIENT_ID=authenticated
3
+ WWEBJS_TEST_MD=1
package/README.md CHANGED
@@ -47,43 +47,14 @@ client.initialize();
47
47
 
48
48
  Take a look at [example.js](https://github.com/pedroslopez/whatsapp-web.js/blob/master/example.js) for another example with more use cases.
49
49
 
50
- ## Remote Access
50
+ For more information on saving and restoring sessions, check out the available [Authentication Strategies](https://wwebjs.dev/guide/authentication.html).
51
51
 
52
- You could also connect to any previously existing browser instance:
53
-
54
- ```js
55
- const client = new Client({
56
- puppeteer: {
57
- browserWSEndpoint: `ws://localhost:3000`
58
- }
59
- });
60
- ```
61
-
62
- ### Docker
63
-
64
- 1) Installing a browser using browserless:
65
-
66
- ```
67
- docker run \
68
- --rm \
69
- -p 3000:3000 \
70
- -e "MAX_CONCURRENT_SESSIONS=1" \
71
- browserless/chrome:latest
72
- ```
73
-
74
- Reference: https://docs.browserless.io/docs/docker-quickstart.html
75
-
76
- ### Remote Debugging
77
-
78
- 2) Running a browser with websocket remote debugging enabled:
79
- > chrome.exe --remote-debugging-port=9222
80
-
81
- After that check the following webpage and check http://127.0.0.1:9220/json and get the **webSocketDebuggerUrl**
82
52
 
83
53
  ## Supported features
84
54
 
85
55
  | Feature | Status |
86
56
  | ------------- | ------------- |
57
+ | Multi Device | ✅ |
87
58
  | Send messages | ✅ |
88
59
  | Receive messages | ✅ |
89
60
  | Send media (images/audio/documents) | ✅ |
@@ -116,11 +87,13 @@ Something missing? Make an issue and let us know!
116
87
 
117
88
  Pull requests are welcome! If you see something you'd like to add, please do. For drastic changes, please open an issue first.
118
89
 
119
- ## Donating
90
+ ## Supporting the project
120
91
 
121
- You can support the maintainer of this project through the link below
92
+ You can support the maintainer of this project through the links below
122
93
 
123
- [![Support via PayPal](https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg)](https://www.paypal.me/psla/)
94
+ - [Support via GitHub Sponsors](https://github.com/sponsors/pedroslopez)
95
+ - [Support via PayPal](https://www.paypal.me/psla/)
96
+ - [Sign up for DigitalOcean](https://m.do.co/c/73f906a36ed4) and get $100 in credit when you sign up (Referral)
124
97
 
125
98
  ## Disclaimer
126
99
 
package/example.js CHANGED
@@ -1,22 +1,9 @@
1
- const fs = require('fs');
2
- const { Client, Location, List, Buttons } = require('./index');
1
+ const { Client, Location, List, Buttons, LocalAuth } = require('./index');
3
2
 
4
- const SESSION_FILE_PATH = './session.json';
5
- let sessionCfg;
6
- if (fs.existsSync(SESSION_FILE_PATH)) {
7
- sessionCfg = require(SESSION_FILE_PATH);
8
- }
9
-
10
- const client = new Client({ puppeteer: { headless: false }, session: sessionCfg });
11
- // You can use an existing session and avoid scanning a QR code by adding a "session" object to the client options.
12
- // This object must include WABrowserId, WASecretBundle, WAToken1 and WAToken2.
13
-
14
- // You also could connect to an existing instance of a browser
15
- // {
16
- // puppeteer: {
17
- // browserWSEndpoint: `ws://localhost:3000`
18
- // }
19
- // }
3
+ const client = new Client({
4
+ authStrategy: new LocalAuth(),
5
+ puppeteer: { headless: false }
6
+ });
20
7
 
21
8
  client.initialize();
22
9
 
@@ -25,18 +12,12 @@ client.on('qr', (qr) => {
25
12
  console.log('QR RECEIVED', qr);
26
13
  });
27
14
 
28
- client.on('authenticated', (session) => {
29
- console.log('AUTHENTICATED', session);
30
- sessionCfg=session;
31
- fs.writeFile(SESSION_FILE_PATH, JSON.stringify(session), function (err) {
32
- if (err) {
33
- console.error(err);
34
- }
35
- });
15
+ client.on('authenticated', () => {
16
+ console.log('AUTHENTICATED');
36
17
  });
37
18
 
38
19
  client.on('auth_failure', msg => {
39
- // Fired if session restore was unsuccessfull
20
+ // Fired if session restore was unsuccessful
40
21
  console.error('AUTHENTICATION FAILURE', msg);
41
22
  });
42
23
 
@@ -124,9 +105,8 @@ client.on('message', async msg => {
124
105
  client.sendMessage(msg.from, `
125
106
  *Connection info*
126
107
  User name: ${info.pushname}
127
- My number: ${info.me.user}
108
+ My number: ${info.wid.user}
128
109
  Platform: ${info.platform}
129
- WhatsApp version: ${info.phone.wa_version}
130
110
  `);
131
111
  } else if (msg.body === '!mediainfo' && msg.hasMedia) {
132
112
  const attachmentData = await msg.downloadMedia();
@@ -267,12 +247,6 @@ client.on('group_update', (notification) => {
267
247
  console.log('update', notification);
268
248
  });
269
249
 
270
- client.on('change_battery', (batteryInfo) => {
271
- // Battery percentage for attached device has changed
272
- const { battery, plugged } = batteryInfo;
273
- console.log(`Battery: ${battery}% - Charging? ${plugged}`);
274
- });
275
-
276
250
  client.on('change_state', state => {
277
251
  console.log('CHANGE STATE', state );
278
252
  });
package/index.d.ts CHANGED
@@ -84,6 +84,9 @@ declare namespace WAWebJS {
84
84
  /** Returns the contact ID's profile picture URL, if privacy settings allow it */
85
85
  getProfilePicUrl(contactId: string): Promise<string>
86
86
 
87
+ /** Gets the Contact's common groups with you. Returns empty array if you don't have any common group. */
88
+ getCommonGroups(contactId: string): Promise<ChatId[]>
89
+
87
90
  /** Gets the current connection state for the client */
88
91
  getState(): Promise<WAState>
89
92
 
@@ -118,6 +121,9 @@ declare namespace WAWebJS {
118
121
  /** Marks the client as online */
119
122
  sendPresenceAvailable(): Promise<void>
120
123
 
124
+ /** Marks the client as offline */
125
+ sendPresenceUnavailable(): Promise<void>
126
+
121
127
  /** Mark as seen for the Chat */
122
128
  sendSeen(chatId: string): Promise<boolean>
123
129
 
@@ -134,7 +140,7 @@ declare namespace WAWebJS {
134
140
  * Sets the current user's display name
135
141
  * @param displayName New display name
136
142
  */
137
- setDisplayName(displayName: string): Promise<void>
143
+ setDisplayName(displayName: string): Promise<boolean>
138
144
 
139
145
  /** Changes and returns the archive state of the Chat */
140
146
  unarchiveChat(chatId: string): Promise<boolean>
@@ -150,11 +156,16 @@ declare namespace WAWebJS {
150
156
 
151
157
  /** Emitted when authentication is successful */
152
158
  on(event: 'authenticated', listener: (
153
- /** Object containing session information. Can be used to restore the session */
154
- session: ClientSession
159
+ /**
160
+ * Object containing session information, when using LegacySessionAuth. Can be used to restore the session
161
+ */
162
+ session?: ClientSession
155
163
  ) => void): this
156
164
 
157
- /** Emitted when the battery percentage for the attached device changes */
165
+ /**
166
+ * Emitted when the battery percentage for the attached device changes
167
+ * @deprecated
168
+ */
158
169
  on(event: 'change_battery', listener: (batteryInfo: BatteryInfo) => void): this
159
170
 
160
171
  /** Emitted when the connection state changes */
@@ -249,14 +260,12 @@ declare namespace WAWebJS {
249
260
 
250
261
  /** Current connection information */
251
262
  export interface ClientInfo {
252
- /**
253
- * Current user ID
254
- * @deprecated Use .wid instead
255
- */
256
- me: ContactId
257
263
  /** Current user ID */
258
264
  wid: ContactId
259
- /** Information about the phone this client is connected to */
265
+ /**
266
+ * Information about the phone this client is connected to. Not available in multi-device.
267
+ * @deprecated
268
+ */
260
269
  phone: ClientInfoPhone
261
270
  /** Platform the phone is running on */
262
271
  platform: string
@@ -267,7 +276,10 @@ declare namespace WAWebJS {
267
276
  getBatteryStatus: () => Promise<BatteryInfo>
268
277
  }
269
278
 
270
- /** Information about the phone this client is connected to */
279
+ /**
280
+ * Information about the phone this client is connected to
281
+ * @deprecated
282
+ */
271
283
  export interface ClientInfoPhone {
272
284
  /** WhatsApp Version running on the phone */
273
285
  wa_version: string
@@ -284,23 +296,22 @@ declare namespace WAWebJS {
284
296
  /** Options for initializing the whatsapp client */
285
297
  export interface ClientOptions {
286
298
  /** Timeout for authentication selector in puppeteer
287
- * @default 45000 */
299
+ * @default 0 */
288
300
  authTimeoutMs?: number,
289
301
  /** Puppeteer launch options. View docs here: https://github.com/puppeteer/puppeteer/ */
290
302
  puppeteer?: puppeteer.LaunchOptions & puppeteer.BrowserLaunchArgumentOptions & puppeteer.BrowserConnectOptions
291
- /** Refresh interval for qr code (how much time to wait before checking if the qr code has changed)
292
- * @default 20000 */
293
- qrRefreshIntervalMs?: number
294
- /** Timeout for qr code selector in puppeteer
295
- * @default 45000 */
296
- qrTimeoutMs?: number,
297
- /** How many times should the qrcode be refreshed before giving up
303
+ /** Determines how to save and restore sessions. Will use LegacySessionAuth if options.session is set. Otherwise, NoAuth will be used. */
304
+ authStrategy?: AuthStrategy,
305
+ /** How many times should the qrcode be refreshed before giving up
298
306
  * @default 0 (disabled) */
299
307
  qrMaxRetries?: number,
300
- /** Restart client with a new session (i.e. use null 'session' var) if authentication fails
301
- * @default false */
308
+ /**
309
+ * @deprecated This option should be set directly on the LegacySessionAuth
310
+ */
302
311
  restartOnAuthFail?: boolean
303
- /** Whatsapp session to restore. If not set, will start a new session */
312
+ /**
313
+ * @deprecated Only here for backwards-compatibility. You should move to using LocalAuth, or set the authStrategy to LegacySessionAuth explicitly.
314
+ */
304
315
  session?: ClientSession
305
316
  /** If another whatsapp web session is detected (another browser), take over the session in the current browser
306
317
  * @default false */
@@ -316,7 +327,52 @@ declare namespace WAWebJS {
316
327
  ffmpegPath?: string
317
328
  }
318
329
 
319
- /** Represents a Whatsapp client session */
330
+ /**
331
+ * Base class which all authentication strategies extend
332
+ */
333
+ export abstract class AuthStrategy {
334
+ setup: (client: Client) => void;
335
+ beforeBrowserInitialized: () => Promise<void>;
336
+ afterBrowserInitialized: () => Promise<void>;
337
+ onAuthenticationNeeded: () => Promise<{
338
+ failed?: boolean;
339
+ restart?: boolean;
340
+ failureEventPayload?: any
341
+ }>;
342
+ getAuthEventPayload: () => Promise<any>;
343
+ logout: () => Promise<void>;
344
+ }
345
+
346
+ /**
347
+ * No session restoring functionality
348
+ * Will need to authenticate via QR code every time
349
+ */
350
+ export class NoAuth extends AuthStrategy {}
351
+
352
+ /**
353
+ * Local directory-based authentication
354
+ */
355
+ export class LocalAuth extends AuthStrategy {
356
+ constructor(options?: {
357
+ clientId?: string,
358
+ dataPath?: string
359
+ })
360
+ }
361
+
362
+ /**
363
+ * Legacy session auth strategy
364
+ * Not compatible with multi-device accounts.
365
+ */
366
+ export class LegacySessionAuth extends AuthStrategy {
367
+ constructor(options?: {
368
+ session?: ClientSession,
369
+ restartOnAuth?: boolean,
370
+ })
371
+ }
372
+
373
+ /**
374
+ * Represents a WhatsApp client session
375
+ */
320
376
  export interface ClientSession {
321
377
  WABrowserId: string,
322
378
  WASecretBundle: string,
@@ -324,6 +380,9 @@ declare namespace WAWebJS {
324
380
  WAToken2: string,
325
381
  }
326
382
 
383
+ /**
384
+ * @deprecated
385
+ */
327
386
  export interface BatteryInfo {
328
387
  /** The current battery percentage */
329
388
  battery: number,
@@ -556,6 +615,8 @@ declare namespace WAWebJS {
556
615
  hasMedia: boolean,
557
616
  /** Indicates if the message was sent as a reply to another message */
558
617
  hasQuotedMsg: boolean,
618
+ /** Indicates the duration of the message in seconds */
619
+ duration: string,
559
620
  /** ID that represents the message */
560
621
  id: MessageId,
561
622
  /** Indicates if the message was forwarded */
@@ -608,6 +669,13 @@ declare namespace WAWebJS {
608
669
  selectedButtonId?: string,
609
670
  /** Selected list row ID */
610
671
  selectedRowId?: string,
672
+ /** Returns message in a raw format */
673
+ rawData: object,
674
+ /*
675
+ * Reloads this Message object's data in-place with the latest values from WhatsApp Web.
676
+ * Note that the Message must still be in the web app cache for this to work, otherwise will return null.
677
+ */
678
+ reload: () => Promise<Message>,
611
679
  /** Accept the Group V4 Invite in message */
612
680
  acceptGroupV4Invite: () => Promise<{status: number}>,
613
681
  /** Deletes the message from the chat */
@@ -679,7 +747,7 @@ declare namespace WAWebJS {
679
747
 
680
748
  /** Options for sending a message */
681
749
  export interface MessageSendOptions {
682
- /** Show links preview */
750
+ /** Show links preview. Has no effect on multi-device accounts. */
683
751
  linkPreview?: boolean
684
752
  /** Send audio as voice message */
685
753
  sendAudioAsVoice?: boolean
@@ -741,7 +809,7 @@ declare namespace WAWebJS {
741
809
  static fromUrl: (url: string, options?: MediaFromURLOptions) => Promise<MessageMedia>
742
810
  }
743
811
 
744
- export type MessageContent = string | MessageMedia | Location | Contact | Contact[] | List | Buttons
812
+ export type MessageContent = string | MessageMedia | Location | Contact | Contact[] | List | Buttons
745
813
 
746
814
  /**
747
815
  * Represents a Contact on WhatsApp
@@ -834,6 +902,9 @@ declare namespace WAWebJS {
834
902
 
835
903
  /** Gets the Contact's current "about" info. Returns null if you don't have permission to read their status. */
836
904
  getAbout: () => Promise<string | null>,
905
+
906
+ /** Gets the Contact's common groups with you. Returns empty array if you don't have any common group. */
907
+ getCommonGroups: () => Promise<ChatId[]>
837
908
 
838
909
  }
839
910
 
@@ -977,7 +1048,7 @@ declare namespace WAWebJS {
977
1048
  }
978
1049
 
979
1050
  /** Promotes or demotes participants by IDs to regular users or admins */
980
- export type ChangeParticipantsPermisions =
1051
+ export type ChangeParticipantsPermissions =
981
1052
  (participantIds: Array<string>) => Promise<{ status: number }>
982
1053
 
983
1054
  /** Adds or removes a list of participants by ID to the group */
@@ -1007,13 +1078,13 @@ declare namespace WAWebJS {
1007
1078
  /** Removes a list of participants by ID to the group */
1008
1079
  removeParticipants: ChangeGroupParticipants;
1009
1080
  /** Promotes participants by IDs to admins */
1010
- promoteParticipants: ChangeParticipantsPermisions;
1081
+ promoteParticipants: ChangeParticipantsPermissions;
1011
1082
  /** Demotes participants by IDs to regular users */
1012
- demoteParticipants: ChangeParticipantsPermisions;
1083
+ demoteParticipants: ChangeParticipantsPermissions;
1013
1084
  /** Updates the group subject */
1014
- setSubject: (subject: string) => Promise<void>;
1085
+ setSubject: (subject: string) => Promise<boolean>;
1015
1086
  /** Updates the group description */
1016
- setDescription: (description: string) => Promise<void>;
1087
+ setDescription: (description: string) => Promise<boolean>;
1017
1088
  /** Updates the group settings to only allow admins to send messages
1018
1089
  * @param {boolean} [adminsOnly=true] Enable or disable this option
1019
1090
  * @returns {Promise<boolean>} Returns true if the setting was properly updated. This can return false if the user does not have the necessary permissions.
package/index.js CHANGED
@@ -21,5 +21,11 @@ module.exports = {
21
21
  ProductMetadata: require('./src/structures/ProductMetadata'),
22
22
  List: require('./src/structures/List'),
23
23
  Buttons: require('./src/structures/Buttons'),
24
+
25
+ // Auth Strategies
26
+ NoAuth: require('./src/authStrategies/NoAuth'),
27
+ LocalAuth: require('./src/authStrategies/LocalAuth'),
28
+ LegacySessionAuth: require('./src/authStrategies/LegacySessionAuth'),
29
+
24
30
  ...Constants
25
31
  };
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "whatsapp-web.js",
3
- "version": "1.15.8",
3
+ "version": "1.16.0",
4
4
  "description": "Library for interacting with the WhatsApp Web API ",
5
5
  "main": "./index.js",
6
6
  "typings": "./index.d.ts",
7
7
  "scripts": {
8
- "test": "mocha tests --recursive",
8
+ "test": "mocha tests --recursive --timeout 5000",
9
9
  "test-single": "mocha",
10
10
  "shell": "node --experimental-repl-await ./shell.js",
11
11
  "generate-docs": "node_modules/.bin/jsdoc --configure .jsdoc.json --verbose"
@@ -35,12 +35,12 @@
35
35
  "mime": "^3.0.0",
36
36
  "node-fetch": "^2.6.5",
37
37
  "node-webpmux": "^3.1.0",
38
- "puppeteer": "^13.0.0",
39
- "sharp": "^0.28.3"
38
+ "puppeteer": "^13.0.0"
40
39
  },
41
40
  "devDependencies": {
42
41
  "@types/node-fetch": "^2.5.12",
43
42
  "chai": "^4.3.4",
43
+ "chai-as-promised": "^7.1.1",
44
44
  "dotenv": "^16.0.0",
45
45
  "eslint": "^8.4.1",
46
46
  "eslint-plugin-mocha": "^10.0.3",
package/shell.js CHANGED
@@ -2,24 +2,17 @@
2
2
  * ==== wwebjs-shell ====
3
3
  * Used for quickly testing library features
4
4
  *
5
- * Running `npm run shell` will start WhatsApp Web in headless mode
5
+ * Running `npm run shell` will start WhatsApp Web with headless=false
6
6
  * and then drop you into Node REPL with `client` in its context.
7
7
  */
8
8
 
9
9
  const repl = require('repl');
10
- const fs = require('fs');
11
10
 
12
- const { Client } = require('./index');
13
-
14
- const SESSION_FILE_PATH = './session.json';
15
- let sessionCfg;
16
- if (fs.existsSync(SESSION_FILE_PATH)) {
17
- sessionCfg = require(SESSION_FILE_PATH);
18
- }
11
+ const { Client, LocalAuth } = require('./index');
19
12
 
20
13
  const client = new Client({
21
14
  puppeteer: { headless: false },
22
- session: sessionCfg
15
+ authStrategy: new LocalAuth()
23
16
  });
24
17
 
25
18
  console.log('Initializing...');
@@ -30,6 +23,10 @@ client.on('qr', () => {
30
23
  console.log('Please scan the QR code on the browser.');
31
24
  });
32
25
 
26
+ client.on('authenticated', (session) => {
27
+ console.log(JSON.stringify(session));
28
+ });
29
+
33
30
  client.on('ready', () => {
34
31
  const shell = repl.start('wwebjs> ');
35
32
  shell.context.client = client;