@another-trial/whatsapp-web.js 1.34.1 → 1.34.5-alpha.3

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.
Files changed (56) hide show
  1. package/.env.example +2 -2
  2. package/CODE_OF_CONDUCT.md +133 -133
  3. package/LICENSE +201 -201
  4. package/README.md +155 -185
  5. package/example.js +706 -690
  6. package/index.d.ts +2259 -2202
  7. package/index.js +35 -35
  8. package/package.json +59 -59
  9. package/shell.js +36 -36
  10. package/src/Client.js +2533 -2361
  11. package/src/authStrategies/BaseAuthStrategy.js +26 -26
  12. package/src/authStrategies/LocalAuth.js +58 -58
  13. package/src/authStrategies/NoAuth.js +11 -11
  14. package/src/authStrategies/RemoteAuth.js +210 -210
  15. package/src/factories/ChatFactory.js +21 -21
  16. package/src/factories/ContactFactory.js +15 -15
  17. package/src/structures/Base.js +21 -21
  18. package/src/structures/Broadcast.js +69 -69
  19. package/src/structures/BusinessContact.js +20 -20
  20. package/src/structures/Buttons.js +81 -81
  21. package/src/structures/Call.js +75 -75
  22. package/src/structures/Channel.js +382 -382
  23. package/src/structures/Chat.js +329 -299
  24. package/src/structures/ClientInfo.js +70 -70
  25. package/src/structures/Contact.js +215 -208
  26. package/src/structures/GroupChat.js +485 -485
  27. package/src/structures/GroupNotification.js +104 -104
  28. package/src/structures/Label.js +49 -49
  29. package/src/structures/List.js +79 -79
  30. package/src/structures/Location.js +61 -61
  31. package/src/structures/Message.js +787 -747
  32. package/src/structures/MessageMedia.js +111 -111
  33. package/src/structures/Order.js +51 -51
  34. package/src/structures/Payment.js +79 -79
  35. package/src/structures/Poll.js +44 -44
  36. package/src/structures/PollVote.js +75 -75
  37. package/src/structures/PrivateChat.js +12 -12
  38. package/src/structures/PrivateContact.js +12 -12
  39. package/src/structures/Product.js +67 -67
  40. package/src/structures/ProductMetadata.js +24 -24
  41. package/src/structures/Reaction.js +68 -68
  42. package/src/structures/ScheduledEvent.js +71 -71
  43. package/src/structures/index.js +27 -27
  44. package/src/util/Constants.js +186 -183
  45. package/src/util/Injected/AuthStore/AuthStore.js +16 -16
  46. package/src/util/Injected/AuthStore/LegacyAuthStore.js +21 -21
  47. package/src/util/Injected/LegacyStore.js +145 -145
  48. package/src/util/Injected/Store.js +263 -233
  49. package/src/util/Injected/Utils.js +1221 -1169
  50. package/src/util/InterfaceController.js +126 -126
  51. package/src/util/Puppeteer.js +23 -23
  52. package/src/util/Util.js +186 -186
  53. package/src/webCache/LocalWebCache.js +40 -40
  54. package/src/webCache/RemoteWebCache.js +39 -39
  55. package/src/webCache/WebCache.js +13 -13
  56. package/src/webCache/WebCacheFactory.js +19 -19
package/example.js CHANGED
@@ -1,690 +1,706 @@
1
- const { Client, Location, Poll, List, Buttons, LocalAuth } = require('./index');
2
-
3
- const client = new Client({
4
- authStrategy: new LocalAuth(),
5
- // proxyAuthentication: { username: 'username', password: 'password' },
6
- /**
7
- * This option changes the browser name from defined in user agent to custom.
8
- */
9
- // deviceName: 'Your custom name',
10
- /**
11
- * This option changes browser type from defined in user agent to yours. It affects the browser icon
12
- * that is displayed in 'linked devices' section.
13
- * Valid value are: 'Chrome' | 'Firefox' | 'IE' | 'Opera' | 'Safari' | 'Edge'.
14
- * If another value is provided, the browser icon in 'linked devices' section will be gray.
15
- */
16
- // browserName: 'Firefox',
17
- puppeteer: {
18
- // args: ['--proxy-server=proxy-server-that-requires-authentication.example.com'],
19
- headless: false,
20
- },
21
- // pairWithPhoneNumber: {
22
- // phoneNumber: '96170100100' // Pair with phone number (format: <COUNTRY_CODE><PHONE_NUMBER>)
23
- // showNotification: true,
24
- // intervalMs: 180000 // Time to renew pairing code in milliseconds, defaults to 3 minutes
25
- // }
26
- });
27
-
28
- // client initialize does not finish at ready now.
29
- client.initialize();
30
-
31
- client.on('loading_screen', (percent, message) => {
32
- console.log('LOADING SCREEN', percent, message);
33
- });
34
-
35
- client.on('qr', async (qr) => {
36
- // NOTE: This event will not be fired if a session is specified.
37
- console.log('QR RECEIVED', qr);
38
- });
39
-
40
- client.on('code', (code) => {
41
- console.log('Pairing code:',code);
42
- });
43
-
44
- client.on('authenticated', () => {
45
- console.log('AUTHENTICATED');
46
- });
47
-
48
- client.on('auth_failure', msg => {
49
- // Fired if session restore was unsuccessful
50
- console.error('AUTHENTICATION FAILURE', msg);
51
- });
52
-
53
- client.on('ready', async () => {
54
- console.log('READY');
55
- const debugWWebVersion = await client.getWWebVersion();
56
- console.log(`WWebVersion = ${debugWWebVersion}`);
57
-
58
- client.pupPage.on('pageerror', function(err) {
59
- console.log('Page error: ' + err.toString());
60
- });
61
- client.pupPage.on('error', function(err) {
62
- console.log('Page error: ' + err.toString());
63
- });
64
-
65
- });
66
-
67
- client.on('message', async msg => {
68
- console.log('MESSAGE RECEIVED', msg);
69
-
70
- if (msg.body === '!ping reply') {
71
- // Send a new message as a reply to the current one
72
- msg.reply('pong');
73
-
74
- } else if (msg.body === '!ping') {
75
- // Send a new message to the same chat
76
- client.sendMessage(msg.from, 'pong');
77
-
78
- } else if (msg.body.startsWith('!sendto ')) {
79
- // Direct send a new message to specific id
80
- let number = msg.body.split(' ')[1];
81
- let messageIndex = msg.body.indexOf(number) + number.length;
82
- let message = msg.body.slice(messageIndex, msg.body.length);
83
- number = number.includes('@c.us') ? number : `${number}@c.us`;
84
- let chat = await msg.getChat();
85
- chat.sendSeen();
86
- client.sendMessage(number, message);
87
-
88
- } else if (msg.body.startsWith('!subject ')) {
89
- // Change the group subject
90
- let chat = await msg.getChat();
91
- if (chat.isGroup) {
92
- let newSubject = msg.body.slice(9);
93
- chat.setSubject(newSubject);
94
- } else {
95
- msg.reply('This command can only be used in a group!');
96
- }
97
- } else if (msg.body.startsWith('!echo ')) {
98
- // Replies with the same message
99
- msg.reply(msg.body.slice(6));
100
- } else if (msg.body.startsWith('!preview ')) {
101
- const text = msg.body.slice(9);
102
- msg.reply(text, null, { linkPreview: true });
103
- } else if (msg.body.startsWith('!desc ')) {
104
- // Change the group description
105
- let chat = await msg.getChat();
106
- if (chat.isGroup) {
107
- let newDescription = msg.body.slice(6);
108
- chat.setDescription(newDescription);
109
- } else {
110
- msg.reply('This command can only be used in a group!');
111
- }
112
- } else if (msg.body === '!leave') {
113
- // Leave the group
114
- let chat = await msg.getChat();
115
- if (chat.isGroup) {
116
- chat.leave();
117
- } else {
118
- msg.reply('This command can only be used in a group!');
119
- }
120
- } else if (msg.body.startsWith('!join ')) {
121
- const inviteCode = msg.body.split(' ')[1];
122
- try {
123
- await client.acceptInvite(inviteCode);
124
- msg.reply('Joined the group!');
125
- } catch (e) {
126
- msg.reply('That invite code seems to be invalid.');
127
- }
128
- } else if (msg.body.startsWith('!addmembers')) {
129
- const group = await msg.getChat();
130
- const result = await group.addParticipants(['number1@c.us', 'number2@c.us', 'number3@c.us']);
131
- /**
132
- * The example of the {@link result} output:
133
- *
134
- * {
135
- * 'number1@c.us': {
136
- * code: 200,
137
- * message: 'The participant was added successfully',
138
- * isInviteV4Sent: false
139
- * },
140
- * 'number2@c.us': {
141
- * code: 403,
142
- * message: 'The participant can be added by sending private invitation only',
143
- * isInviteV4Sent: true
144
- * },
145
- * 'number3@c.us': {
146
- * code: 404,
147
- * message: 'The phone number is not registered on WhatsApp',
148
- * isInviteV4Sent: false
149
- * }
150
- * }
151
- *
152
- * For more usage examples:
153
- * @see https://github.com/pedroslopez/whatsapp-web.js/pull/2344#usage-example1
154
- */
155
- console.log(result);
156
- } else if (msg.body === '!creategroup') {
157
- const partitipantsToAdd = ['number1@c.us', 'number2@c.us', 'number3@c.us'];
158
- const result = await client.createGroup('Group Title', partitipantsToAdd);
159
- /**
160
- * The example of the {@link result} output:
161
- * {
162
- * title: 'Group Title',
163
- * gid: {
164
- * server: 'g.us',
165
- * user: '1111111111',
166
- * _serialized: '1111111111@g.us'
167
- * },
168
- * participants: {
169
- * 'botNumber@c.us': {
170
- * statusCode: 200,
171
- * message: 'The participant was added successfully',
172
- * isGroupCreator: true,
173
- * isInviteV4Sent: false
174
- * },
175
- * 'number1@c.us': {
176
- * statusCode: 200,
177
- * message: 'The participant was added successfully',
178
- * isGroupCreator: false,
179
- * isInviteV4Sent: false
180
- * },
181
- * 'number2@c.us': {
182
- * statusCode: 403,
183
- * message: 'The participant can be added by sending private invitation only',
184
- * isGroupCreator: false,
185
- * isInviteV4Sent: true
186
- * },
187
- * 'number3@c.us': {
188
- * statusCode: 404,
189
- * message: 'The phone number is not registered on WhatsApp',
190
- * isGroupCreator: false,
191
- * isInviteV4Sent: false
192
- * }
193
- * }
194
- * }
195
- *
196
- * For more usage examples:
197
- * @see https://github.com/pedroslopez/whatsapp-web.js/pull/2344#usage-example2
198
- */
199
- console.log(result);
200
- } else if (msg.body === '!groupinfo') {
201
- let chat = await msg.getChat();
202
- if (chat.isGroup) {
203
- msg.reply(`
204
- *Group Details*
205
- Name: ${chat.name}
206
- Description: ${chat.description}
207
- Created At: ${chat.createdAt.toString()}
208
- Created By: ${chat.owner.user}
209
- Participant count: ${chat.participants.length}
210
- `);
211
- } else {
212
- msg.reply('This command can only be used in a group!');
213
- }
214
- } else if (msg.body === '!chats') {
215
- const chats = await client.getChats();
216
- client.sendMessage(msg.from, `The bot has ${chats.length} chats open.`);
217
- } else if (msg.body === '!info') {
218
- let info = client.info;
219
- client.sendMessage(msg.from, `
220
- *Connection info*
221
- User name: ${info.pushname}
222
- My number: ${info.wid.user}
223
- Platform: ${info.platform}
224
- `);
225
- } else if (msg.body === '!mediainfo' && msg.hasMedia) {
226
- const attachmentData = await msg.downloadMedia();
227
- msg.reply(`
228
- *Media info*
229
- MimeType: ${attachmentData.mimetype}
230
- Filename: ${attachmentData.filename}
231
- Data (length): ${attachmentData.data.length}
232
- `);
233
- } else if (msg.body === '!quoteinfo' && msg.hasQuotedMsg) {
234
- const quotedMsg = await msg.getQuotedMessage();
235
-
236
- quotedMsg.reply(`
237
- ID: ${quotedMsg.id._serialized}
238
- Type: ${quotedMsg.type}
239
- Author: ${quotedMsg.author || quotedMsg.from}
240
- Timestamp: ${quotedMsg.timestamp}
241
- Has Media? ${quotedMsg.hasMedia}
242
- `);
243
- } else if (msg.body === '!resendmedia' && msg.hasQuotedMsg) {
244
- const quotedMsg = await msg.getQuotedMessage();
245
- if (quotedMsg.hasMedia) {
246
- const attachmentData = await quotedMsg.downloadMedia();
247
- client.sendMessage(msg.from, attachmentData, { caption: 'Here\'s your requested media.' });
248
- }
249
- if (quotedMsg.hasMedia && quotedMsg.type === 'audio') {
250
- const audio = await quotedMsg.downloadMedia();
251
- await client.sendMessage(msg.from, audio, { sendAudioAsVoice: true });
252
- }
253
- } else if (msg.body === '!isviewonce' && msg.hasQuotedMsg) {
254
- const quotedMsg = await msg.getQuotedMessage();
255
- if (quotedMsg.hasMedia) {
256
- const media = await quotedMsg.downloadMedia();
257
- await client.sendMessage(msg.from, media, { isViewOnce: true });
258
- }
259
- } else if (msg.body === '!location') {
260
- // only latitude and longitude
261
- await msg.reply(new Location(37.422, -122.084));
262
- // location with name only
263
- await msg.reply(new Location(37.422, -122.084, { name: 'Googleplex' }));
264
- // location with address only
265
- await msg.reply(new Location(37.422, -122.084, { address: '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA' }));
266
- // location with name, address and url
267
- await msg.reply(new Location(37.422, -122.084, { name: 'Googleplex', address: '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA', url: 'https://google.com' }));
268
- } else if (msg.location) {
269
- msg.reply(msg.location);
270
- } else if (msg.body.startsWith('!status ')) {
271
- const newStatus = msg.body.split(' ')[1];
272
- await client.setStatus(newStatus);
273
- msg.reply(`Status was updated to *${newStatus}*`);
274
- } else if (msg.body === '!mentionUsers') {
275
- const chat = await msg.getChat();
276
- const userNumber = 'XXXXXXXXXX';
277
- /**
278
- * To mention one user you can pass user's ID to 'mentions' property as is,
279
- * without wrapping it in Array, and a user's phone number to the message body:
280
- */
281
- await chat.sendMessage(`Hi @${userNumber}`, {
282
- mentions: userNumber + '@c.us'
283
- });
284
- // To mention a list of users:
285
- await chat.sendMessage(`Hi @${userNumber}, @${userNumber}`, {
286
- mentions: [userNumber + '@c.us', userNumber + '@c.us']
287
- });
288
- } else if (msg.body === '!mentionGroups') {
289
- const chat = await msg.getChat();
290
- const groupId = 'YYYYYYYYYY@g.us';
291
- /**
292
- * Sends clickable group mentions, the same as user mentions.
293
- * When the mentions are clicked, it opens a chat with the mentioned group.
294
- * The 'groupMentions.subject' can be custom
295
- *
296
- * @note The user that does not participate in the mentioned group,
297
- * will not be able to click on that mentioned group, the same if the group does not exist
298
- *
299
- * To mention one group:
300
- */
301
- await chat.sendMessage(`Check the last message here: @${groupId}`, {
302
- groupMentions: { subject: 'GroupSubject', id: groupId }
303
- });
304
- // To mention a list of groups:
305
- await chat.sendMessage(`Check the last message in these groups: @${groupId}, @${groupId}`, {
306
- groupMentions: [
307
- { subject: 'FirstGroup', id: groupId },
308
- { subject: 'SecondGroup', id: groupId }
309
- ]
310
- });
311
- } else if (msg.body === '!getGroupMentions') {
312
- // To get group mentions from a message:
313
- const groupId = 'ZZZZZZZZZZ@g.us';
314
- const msg = await client.sendMessage('chatId', `Check the last message here: @${groupId}`, {
315
- groupMentions: { subject: 'GroupSubject', id: groupId }
316
- });
317
- /** {@link groupMentions} is an array of `GroupChat` */
318
- const groupMentions = await msg.getGroupMentions();
319
- console.log(groupMentions);
320
- } else if (msg.body === '!delete') {
321
- if (msg.hasQuotedMsg) {
322
- const quotedMsg = await msg.getQuotedMessage();
323
- if (quotedMsg.fromMe) {
324
- quotedMsg.delete(true);
325
- } else {
326
- msg.reply('I can only delete my own messages');
327
- }
328
- }
329
- } else if (msg.body === '!pin') {
330
- const chat = await msg.getChat();
331
- await chat.pin();
332
- } else if (msg.body === '!archive') {
333
- const chat = await msg.getChat();
334
- await chat.archive();
335
- } else if (msg.body === '!mute') {
336
- const chat = await msg.getChat();
337
- // mute the chat for 20 seconds
338
- const unmuteDate = new Date();
339
- unmuteDate.setSeconds(unmuteDate.getSeconds() + 20);
340
- await chat.mute(unmuteDate);
341
- } else if (msg.body === '!typing') {
342
- const chat = await msg.getChat();
343
- // simulates typing in the chat
344
- chat.sendStateTyping();
345
- } else if (msg.body === '!recording') {
346
- const chat = await msg.getChat();
347
- // simulates recording audio in the chat
348
- chat.sendStateRecording();
349
- } else if (msg.body === '!clearstate') {
350
- const chat = await msg.getChat();
351
- // stops typing or recording in the chat
352
- chat.clearState();
353
- } else if (msg.body === '!jumpto') {
354
- if (msg.hasQuotedMsg) {
355
- const quotedMsg = await msg.getQuotedMessage();
356
- client.interface.openChatWindowAt(quotedMsg.id._serialized);
357
- }
358
- } else if (msg.body === '!buttons') {
359
- let button = new Buttons('Button body', [{ body: 'bt1' }, { body: 'bt2' }, { body: 'bt3' }], 'title', 'footer');
360
- client.sendMessage(msg.from, button);
361
- } else if (msg.body === '!list') {
362
- let sections = [
363
- { title: 'sectionTitle', rows: [{ title: 'ListItem1', description: 'desc' }, { title: 'ListItem2' }] }
364
- ];
365
- let list = new List('List body', 'btnText', sections, 'Title', 'footer');
366
- client.sendMessage(msg.from, list);
367
- } else if (msg.body === '!reaction') {
368
- msg.react('👍');
369
- } else if (msg.body === '!sendpoll') {
370
- /** By default the poll is created as a single choice poll: */
371
- await msg.reply(new Poll('Winter or Summer?', ['Winter', 'Summer']));
372
- /** If you want to provide a multiple choice poll, add allowMultipleAnswers as true: */
373
- await msg.reply(new Poll('Cats or Dogs?', ['Cats', 'Dogs'], { allowMultipleAnswers: true }));
374
- /**
375
- * You can provide a custom message secret, it can be used as a poll ID:
376
- * @note It has to be a unique vector with a length of 32
377
- */
378
- await msg.reply(
379
- new Poll('Cats or Dogs?', ['Cats', 'Dogs'], {
380
- messageSecret: [
381
- 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
382
- ]
383
- })
384
- );
385
- } else if (msg.body === '!edit') {
386
- if (msg.hasQuotedMsg) {
387
- const quotedMsg = await msg.getQuotedMessage();
388
- if (quotedMsg.fromMe) {
389
- quotedMsg.edit(msg.body.replace('!edit', ''));
390
- } else {
391
- msg.reply('I can only edit my own messages');
392
- }
393
- }
394
- } else if (msg.body === '!updatelabels') {
395
- const chat = await msg.getChat();
396
- await chat.changeLabels([0, 1]);
397
- } else if (msg.body === '!addlabels') {
398
- const chat = await msg.getChat();
399
- let labels = (await chat.getLabels()).map((l) => l.id);
400
- labels.push('0');
401
- labels.push('1');
402
- await chat.changeLabels(labels);
403
- } else if (msg.body === '!removelabels') {
404
- const chat = await msg.getChat();
405
- await chat.changeLabels([]);
406
- } else if (msg.body === '!approverequest') {
407
- /**
408
- * Presented an example for membership request approvals, the same examples are for the request rejections.
409
- * To approve the membership request from a specific user:
410
- */
411
- await client.approveGroupMembershipRequests(msg.from, { requesterIds: 'number@c.us' });
412
- /** The same for execution on group object (no need to provide the group ID): */
413
- const group = await msg.getChat();
414
- await group.approveGroupMembershipRequests({ requesterIds: 'number@c.us' });
415
- /** To approve several membership requests: */
416
- const approval = await client.approveGroupMembershipRequests(msg.from, {
417
- requesterIds: ['number1@c.us', 'number2@c.us']
418
- });
419
- /**
420
- * The example of the {@link approval} output:
421
- * [
422
- * {
423
- * requesterId: 'number1@c.us',
424
- * message: 'Rejected successfully'
425
- * },
426
- * {
427
- * requesterId: 'number2@c.us',
428
- * error: 404,
429
- * message: 'ParticipantRequestNotFoundError'
430
- * }
431
- * ]
432
- *
433
- */
434
- console.log(approval);
435
- /** To approve all the existing membership requests (simply don't provide any user IDs): */
436
- await client.approveGroupMembershipRequests(msg.from);
437
- /** To change the sleep value to 300 ms: */
438
- await client.approveGroupMembershipRequests(msg.from, {
439
- requesterIds: ['number1@c.us', 'number2@c.us'],
440
- sleep: 300
441
- });
442
- /** To change the sleep value to random value between 100 and 300 ms: */
443
- await client.approveGroupMembershipRequests(msg.from, {
444
- requesterIds: ['number1@c.us', 'number2@c.us'],
445
- sleep: [100, 300]
446
- });
447
- /** To explicitly disable the sleep: */
448
- await client.approveGroupMembershipRequests(msg.from, {
449
- requesterIds: ['number1@c.us', 'number2@c.us'],
450
- sleep: null
451
- });
452
- } else if (msg.body === '!pinmsg') {
453
- /**
454
- * Pins a message in a chat, a method takes a number in seconds for the message to be pinned.
455
- * WhatsApp default values for duration to pass to the method are:
456
- * 1. 86400 for 24 hours
457
- * 2. 604800 for 7 days
458
- * 3. 2592000 for 30 days
459
- * You can pass your own value:
460
- */
461
- const result = await msg.pin(60); // Will pin a message for 1 minute
462
- console.log(result); // True if the operation completed successfully, false otherwise
463
- } else if (msg.body === '!howManyConnections') {
464
- /**
465
- * Get user device count by ID
466
- * Each WaWeb Connection counts as one device, and the phone (if exists) counts as one
467
- * So for a non-enterprise user with one WaWeb connection it should return "2"
468
- */
469
- let deviceCount = await client.getContactDeviceCount(msg.from);
470
- await msg.reply(`You have *${deviceCount}* devices connected`);
471
- } else if (msg.body === '!syncHistory') {
472
- const isSynced = await client.syncHistory(msg.from);
473
- // Or through the Chat object:
474
- // const chat = await client.getChatById(msg.from);
475
- // const isSynced = await chat.syncHistory();
476
-
477
- await msg.reply(isSynced ? 'Historical chat is syncing..' : 'There is no historical chat to sync.');
478
- } else if (msg.body === '!statuses') {
479
- const statuses = await client.getBroadcasts();
480
- console.log(statuses);
481
- const chat = await statuses[0]?.getChat(); // Get user chat of a first status
482
- console.log(chat);
483
- } else if (msg.body === '!sendMediaHD' && msg.hasQuotedMsg) {
484
- const quotedMsg = await msg.getQuotedMessage();
485
- if (quotedMsg.hasMedia) {
486
- const media = await quotedMsg.downloadMedia();
487
- await client.sendMessage(msg.from, media, { sendMediaAsHd: true });
488
- }
489
- } else if (msg.body === '!parseVCard') {
490
- const vCard =
491
- 'BEGIN:VCARD\n' +
492
- 'VERSION:3.0\n' +
493
- 'FN:John Doe\n' +
494
- 'ORG:Microsoft;\n' +
495
- 'EMAIL;type=INTERNET:john.doe@gmail.com\n' +
496
- 'URL:www.johndoe.com\n' +
497
- 'TEL;type=CELL;type=VOICE;waid=18006427676:+1 (800) 642 7676\n' +
498
- 'END:VCARD';
499
- const vCardExtended =
500
- 'BEGIN:VCARD\n' +
501
- 'VERSION:3.0\n' +
502
- 'FN:John Doe\n' +
503
- 'ORG:Microsoft;\n' +
504
- 'item1.TEL:+1 (800) 642 7676\n' +
505
- 'item1.X-ABLabel:USA Customer Service\n' +
506
- 'item2.TEL:+55 11 4706 0900\n' +
507
- 'item2.X-ABLabel:Brazil Customer Service\n' +
508
- 'PHOTO;BASE64:here you can paste a binary data of a contact photo in Base64 encoding\n' +
509
- 'END:VCARD';
510
- const userId = 'XXXXXXXXXX@c.us';
511
- await client.sendMessage(userId, vCard);
512
- await client.sendMessage(userId, vCardExtended);
513
- } else if (msg.body === '!changeSync') {
514
- // NOTE: this action will take effect after you restart the client.
515
- const backgroundSync = await client.setBackgroundSync(true);
516
- console.log(backgroundSync);
517
- }
518
- });
519
-
520
- client.on('message_create', async (msg) => {
521
- // Fired on all message creations, including your own
522
- if (msg.fromMe) {
523
- // do stuff here
524
- }
525
-
526
- // Unpins a message
527
- if (msg.fromMe && msg.body.startsWith('!unpin')) {
528
- const pinnedMsg = await msg.getQuotedMessage();
529
- if (pinnedMsg) {
530
- // Will unpin a message
531
- const result = await pinnedMsg.unpin();
532
- console.log(result); // True if the operation completed successfully, false otherwise
533
- }
534
- }
535
- });
536
-
537
- client.on('message_ciphertext', (msg) => {
538
- // Receiving new incoming messages that have been encrypted
539
- // msg.type === 'ciphertext'
540
- msg.body = 'Waiting for this message. Check your phone.';
541
-
542
- // do stuff here
543
- });
544
-
545
- client.on('message_revoke_everyone', async (after, before) => {
546
- // Fired whenever a message is deleted by anyone (including you)
547
- console.log(after); // message after it was deleted.
548
- if (before) {
549
- console.log(before); // message before it was deleted.
550
- }
551
- });
552
-
553
- client.on('message_revoke_me', async (msg) => {
554
- // Fired whenever a message is only deleted in your own view.
555
- console.log(msg.body); // message before it was deleted.
556
- });
557
-
558
- client.on('message_ack', (msg, ack) => {
559
- /*
560
- == ACK VALUES ==
561
- ACK_ERROR: -1
562
- ACK_PENDING: 0
563
- ACK_SERVER: 1
564
- ACK_DEVICE: 2
565
- ACK_READ: 3
566
- ACK_PLAYED: 4
567
- */
568
-
569
- if (ack == 3) {
570
- // The message was read
571
- }
572
- });
573
-
574
- client.on('group_join', (notification) => {
575
- // User has joined or been added to the group.
576
- console.log('join', notification);
577
- notification.reply('User joined.');
578
- });
579
-
580
- client.on('group_leave', (notification) => {
581
- // User has left or been kicked from the group.
582
- console.log('leave', notification);
583
- notification.reply('User left.');
584
- });
585
-
586
- client.on('group_update', (notification) => {
587
- // Group picture, subject or description has been updated.
588
- console.log('update', notification);
589
- });
590
-
591
- client.on('change_state', state => {
592
- console.log('CHANGE STATE', state);
593
- });
594
-
595
- // Change to false if you don't want to reject incoming calls
596
- let rejectCalls = true;
597
-
598
- client.on('call', async (call) => {
599
- console.log('Call received, rejecting. GOTO Line 261 to disable', call);
600
- if (rejectCalls) await call.reject();
601
- await client.sendMessage(call.from, `[${call.fromMe ? 'Outgoing' : 'Incoming'}] Phone call from ${call.from}, type ${call.isGroup ? 'group' : ''} ${call.isVideo ? 'video' : 'audio'} call. ${rejectCalls ? 'This call was automatically rejected by the script.' : ''}`);
602
- });
603
-
604
- client.on('disconnected', (reason) => {
605
- console.log('Client was logged out', reason);
606
- });
607
-
608
- client.on('contact_changed', async (message, oldId, newId, isContact) => {
609
- /** The time the event occurred. */
610
- const eventTime = (new Date(message.timestamp * 1000)).toLocaleString();
611
-
612
- console.log(
613
- `The contact ${oldId.slice(0, -5)}` +
614
- `${!isContact ? ' that participates in group ' +
615
- `${(await client.getChatById(message.to ?? message.from)).name} ` : ' '}` +
616
- `changed their phone number\nat ${eventTime}.\n` +
617
- `Their new phone number is ${newId.slice(0, -5)}.\n`);
618
-
619
- /**
620
- * Information about the @param {message}:
621
- *
622
- * 1. If a notification was emitted due to a group participant changing their phone number:
623
- * @param {message.author} is a participant's id before the change.
624
- * @param {message.recipients[0]} is a participant's id after the change (a new one).
625
- *
626
- * 1.1 If the contact who changed their number WAS in the current user's contact list at the time of the change:
627
- * @param {message.to} is a group chat id the event was emitted in.
628
- * @param {message.from} is a current user's id that got an notification message in the group.
629
- * Also the @param {message.fromMe} is TRUE.
630
- *
631
- * 1.2 Otherwise:
632
- * @param {message.from} is a group chat id the event was emitted in.
633
- * @param {message.to} is @type {undefined}.
634
- * Also @param {message.fromMe} is FALSE.
635
- *
636
- * 2. If a notification was emitted due to a contact changing their phone number:
637
- * @param {message.templateParams} is an array of two user's ids:
638
- * the old (before the change) and a new one, stored in alphabetical order.
639
- * @param {message.from} is a current user's id that has a chat with a user,
640
- * whos phone number was changed.
641
- * @param {message.to} is a user's id (after the change), the current user has a chat with.
642
- */
643
- });
644
-
645
- client.on('group_admin_changed', (notification) => {
646
- if (notification.type === 'promote') {
647
- /**
648
- * Emitted when a current user is promoted to an admin.
649
- * {@link notification.author} is a user who performs the action of promoting/demoting the current user.
650
- */
651
- console.log(`You were promoted by ${notification.author}`);
652
- } else if (notification.type === 'demote')
653
- /** Emitted when a current user is demoted to a regular user. */
654
- console.log(`You were demoted by ${notification.author}`);
655
- });
656
-
657
- client.on('group_membership_request', async (notification) => {
658
- /**
659
- * The example of the {@link notification} output:
660
- * {
661
- * id: {
662
- * fromMe: false,
663
- * remote: 'groupId@g.us',
664
- * id: '123123123132132132',
665
- * participant: 'number@c.us',
666
- * _serialized: 'false_groupId@g.us_123123123132132132_number@c.us'
667
- * },
668
- * body: '',
669
- * type: 'created_membership_requests',
670
- * timestamp: 1694456538,
671
- * chatId: 'groupId@g.us',
672
- * author: 'number@c.us',
673
- * recipientIds: []
674
- * }
675
- *
676
- */
677
- console.log(notification);
678
- /** You can approve or reject the newly appeared membership request: */
679
- await client.approveGroupMembershipRequestss(notification.chatId, notification.author);
680
- await client.rejectGroupMembershipRequests(notification.chatId, notification.author);
681
- });
682
-
683
- client.on('message_reaction', async (reaction) => {
684
- console.log('REACTION RECEIVED', reaction);
685
- });
686
-
687
- client.on('vote_update', (vote) => {
688
- /** The vote that was affected: */
689
- console.log(vote);
690
- });
1
+ const { Client, Location, Poll, List, Buttons, LocalAuth } = require('./index');
2
+
3
+ const client = new Client({
4
+ authStrategy: new LocalAuth(),
5
+ // proxyAuthentication: { username: 'username', password: 'password' },
6
+ /**
7
+ * This option changes the browser name from defined in user agent to custom.
8
+ */
9
+ // deviceName: 'Your custom name',
10
+ /**
11
+ * This option changes browser type from defined in user agent to yours. It affects the browser icon
12
+ * that is displayed in 'linked devices' section.
13
+ * Valid value are: 'Chrome' | 'Firefox' | 'IE' | 'Opera' | 'Safari' | 'Edge'.
14
+ * If another value is provided, the browser icon in 'linked devices' section will be gray.
15
+ */
16
+ // browserName: 'Firefox',
17
+ puppeteer: {
18
+ // args: ['--proxy-server=proxy-server-that-requires-authentication.example.com'],
19
+ headless: false,
20
+ },
21
+ // pairWithPhoneNumber: {
22
+ // phoneNumber: '96170100100' // Pair with phone number (format: <COUNTRY_CODE><PHONE_NUMBER>)
23
+ // showNotification: true,
24
+ // intervalMs: 180000 // Time to renew pairing code in milliseconds, defaults to 3 minutes
25
+ // }
26
+ });
27
+
28
+ // client initialize does not finish at ready now.
29
+ client.initialize();
30
+
31
+ client.on('loading_screen', (percent, message) => {
32
+ console.log('LOADING SCREEN', percent, message);
33
+ });
34
+
35
+ client.on('qr', async (qr) => {
36
+ // NOTE: This event will not be fired if a session is specified.
37
+ console.log('QR RECEIVED', qr);
38
+ });
39
+
40
+ client.on('code', (code) => {
41
+ console.log('Pairing code:',code);
42
+ });
43
+
44
+ client.on('authenticated', () => {
45
+ console.log('AUTHENTICATED');
46
+ });
47
+
48
+ client.on('auth_failure', msg => {
49
+ // Fired if session restore was unsuccessful
50
+ console.error('AUTHENTICATION FAILURE', msg);
51
+ });
52
+
53
+ client.on('ready', async () => {
54
+ console.log('READY');
55
+ const debugWWebVersion = await client.getWWebVersion();
56
+ console.log(`WWebVersion = ${debugWWebVersion}`);
57
+
58
+ client.pupPage.on('pageerror', function(err) {
59
+ console.log('Page error: ' + err.toString());
60
+ });
61
+ client.pupPage.on('error', function(err) {
62
+ console.log('Page error: ' + err.toString());
63
+ });
64
+
65
+ });
66
+
67
+ client.on('message', async msg => {
68
+ console.log('MESSAGE RECEIVED', msg);
69
+
70
+ if (msg.body === '!ping reply') {
71
+ // Send a new message as a reply to the current one
72
+ msg.reply('pong');
73
+
74
+ } else if (msg.body === '!ping') {
75
+ // Send a new message to the same chat
76
+ client.sendMessage(msg.from, 'pong');
77
+
78
+ } else if (msg.body.startsWith('!sendto ')) {
79
+ // Direct send a new message to specific id
80
+ let number = msg.body.split(' ')[1];
81
+ let messageIndex = msg.body.indexOf(number) + number.length;
82
+ let message = msg.body.slice(messageIndex, msg.body.length);
83
+ number = number.includes('@c.us') ? number : `${number}@c.us`;
84
+ let chat = await msg.getChat();
85
+ chat.sendSeen();
86
+ client.sendMessage(number, message);
87
+
88
+ } else if (msg.body.startsWith('!subject ')) {
89
+ // Change the group subject
90
+ let chat = await msg.getChat();
91
+ if (chat.isGroup) {
92
+ let newSubject = msg.body.slice(9);
93
+ chat.setSubject(newSubject);
94
+ } else {
95
+ msg.reply('This command can only be used in a group!');
96
+ }
97
+ } else if (msg.body.startsWith('!echo ')) {
98
+ // Replies with the same message
99
+ msg.reply(msg.body.slice(6));
100
+ } else if (msg.body.startsWith('!preview ')) {
101
+ const text = msg.body.slice(9);
102
+ msg.reply(text, null, { linkPreview: true });
103
+ } else if (msg.body.startsWith('!desc ')) {
104
+ // Change the group description
105
+ let chat = await msg.getChat();
106
+ if (chat.isGroup) {
107
+ let newDescription = msg.body.slice(6);
108
+ chat.setDescription(newDescription);
109
+ } else {
110
+ msg.reply('This command can only be used in a group!');
111
+ }
112
+ } else if (msg.body === '!leave') {
113
+ // Leave the group
114
+ let chat = await msg.getChat();
115
+ if (chat.isGroup) {
116
+ chat.leave();
117
+ } else {
118
+ msg.reply('This command can only be used in a group!');
119
+ }
120
+ } else if (msg.body.startsWith('!join ')) {
121
+ const inviteCode = msg.body.split(' ')[1];
122
+ try {
123
+ await client.acceptInvite(inviteCode);
124
+ msg.reply('Joined the group!');
125
+ } catch (e) {
126
+ msg.reply('That invite code seems to be invalid.');
127
+ }
128
+ } else if (msg.body.startsWith('!addmembers')) {
129
+ const group = await msg.getChat();
130
+ const result = await group.addParticipants(['number1@c.us', 'number2@c.us', 'number3@c.us']);
131
+ /**
132
+ * The example of the {@link result} output:
133
+ *
134
+ * {
135
+ * 'number1@c.us': {
136
+ * code: 200,
137
+ * message: 'The participant was added successfully',
138
+ * isInviteV4Sent: false
139
+ * },
140
+ * 'number2@c.us': {
141
+ * code: 403,
142
+ * message: 'The participant can be added by sending private invitation only',
143
+ * isInviteV4Sent: true
144
+ * },
145
+ * 'number3@c.us': {
146
+ * code: 404,
147
+ * message: 'The phone number is not registered on WhatsApp',
148
+ * isInviteV4Sent: false
149
+ * }
150
+ * }
151
+ *
152
+ * For more usage examples:
153
+ * @see https://github.com/pedroslopez/whatsapp-web.js/pull/2344#usage-example1
154
+ */
155
+ console.log(result);
156
+ } else if (msg.body === '!creategroup') {
157
+ const partitipantsToAdd = ['number1@c.us', 'number2@c.us', 'number3@c.us'];
158
+ const result = await client.createGroup('Group Title', partitipantsToAdd);
159
+ /**
160
+ * The example of the {@link result} output:
161
+ * {
162
+ * title: 'Group Title',
163
+ * gid: {
164
+ * server: 'g.us',
165
+ * user: '1111111111',
166
+ * _serialized: '1111111111@g.us'
167
+ * },
168
+ * participants: {
169
+ * 'botNumber@c.us': {
170
+ * statusCode: 200,
171
+ * message: 'The participant was added successfully',
172
+ * isGroupCreator: true,
173
+ * isInviteV4Sent: false
174
+ * },
175
+ * 'number1@c.us': {
176
+ * statusCode: 200,
177
+ * message: 'The participant was added successfully',
178
+ * isGroupCreator: false,
179
+ * isInviteV4Sent: false
180
+ * },
181
+ * 'number2@c.us': {
182
+ * statusCode: 403,
183
+ * message: 'The participant can be added by sending private invitation only',
184
+ * isGroupCreator: false,
185
+ * isInviteV4Sent: true
186
+ * },
187
+ * 'number3@c.us': {
188
+ * statusCode: 404,
189
+ * message: 'The phone number is not registered on WhatsApp',
190
+ * isGroupCreator: false,
191
+ * isInviteV4Sent: false
192
+ * }
193
+ * }
194
+ * }
195
+ *
196
+ * For more usage examples:
197
+ * @see https://github.com/pedroslopez/whatsapp-web.js/pull/2344#usage-example2
198
+ */
199
+ console.log(result);
200
+ } else if (msg.body === '!groupinfo') {
201
+ let chat = await msg.getChat();
202
+ if (chat.isGroup) {
203
+ msg.reply(`
204
+ *Group Details*
205
+ Name: ${chat.name}
206
+ Description: ${chat.description}
207
+ Created At: ${chat.createdAt.toString()}
208
+ Created By: ${chat.owner.user}
209
+ Participant count: ${chat.participants.length}
210
+ `);
211
+ } else {
212
+ msg.reply('This command can only be used in a group!');
213
+ }
214
+ } else if (msg.body === '!chats') {
215
+ const chats = await client.getChats();
216
+ client.sendMessage(msg.from, `The bot has ${chats.length} chats open.`);
217
+ } else if (msg.body === '!info') {
218
+ let info = client.info;
219
+ client.sendMessage(msg.from, `
220
+ *Connection info*
221
+ User name: ${info.pushname}
222
+ My number: ${info.wid.user}
223
+ Platform: ${info.platform}
224
+ `);
225
+ } else if (msg.body === '!mediainfo' && msg.hasMedia) {
226
+ const attachmentData = await msg.downloadMedia();
227
+ msg.reply(`
228
+ *Media info*
229
+ MimeType: ${attachmentData.mimetype}
230
+ Filename: ${attachmentData.filename}
231
+ Data (length): ${attachmentData.data.length}
232
+ `);
233
+ } else if (msg.body === '!quoteinfo' && msg.hasQuotedMsg) {
234
+ const quotedMsg = await msg.getQuotedMessage();
235
+
236
+ quotedMsg.reply(`
237
+ ID: ${quotedMsg.id._serialized}
238
+ Type: ${quotedMsg.type}
239
+ Author: ${quotedMsg.author || quotedMsg.from}
240
+ Timestamp: ${quotedMsg.timestamp}
241
+ Has Media? ${quotedMsg.hasMedia}
242
+ `);
243
+ } else if (msg.body === '!resendmedia' && msg.hasQuotedMsg) {
244
+ const quotedMsg = await msg.getQuotedMessage();
245
+ if (quotedMsg.hasMedia) {
246
+ const attachmentData = await quotedMsg.downloadMedia();
247
+ client.sendMessage(msg.from, attachmentData, { caption: 'Here\'s your requested media.' });
248
+ }
249
+ if (quotedMsg.hasMedia && quotedMsg.type === 'audio') {
250
+ const audio = await quotedMsg.downloadMedia();
251
+ await client.sendMessage(msg.from, audio, { sendAudioAsVoice: true });
252
+ }
253
+ } else if (msg.body === '!isviewonce' && msg.hasQuotedMsg) {
254
+ const quotedMsg = await msg.getQuotedMessage();
255
+ if (quotedMsg.hasMedia) {
256
+ const media = await quotedMsg.downloadMedia();
257
+ await client.sendMessage(msg.from, media, { isViewOnce: true });
258
+ }
259
+ } else if (msg.body === '!location') {
260
+ // only latitude and longitude
261
+ await msg.reply(new Location(37.422, -122.084));
262
+ // location with name only
263
+ await msg.reply(new Location(37.422, -122.084, { name: 'Googleplex' }));
264
+ // location with address only
265
+ await msg.reply(new Location(37.422, -122.084, { address: '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA' }));
266
+ // location with name, address and url
267
+ await msg.reply(new Location(37.422, -122.084, { name: 'Googleplex', address: '1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA', url: 'https://google.com' }));
268
+ } else if (msg.location) {
269
+ msg.reply(msg.location);
270
+ } else if (msg.body.startsWith('!status ')) {
271
+ const newStatus = msg.body.split(' ')[1];
272
+ await client.setStatus(newStatus);
273
+ msg.reply(`Status was updated to *${newStatus}*`);
274
+ } else if (msg.body === '!mentionUsers') {
275
+ const chat = await msg.getChat();
276
+ const userNumber = 'XXXXXXXXXX';
277
+ /**
278
+ * To mention one user you can pass user's ID to 'mentions' property as is,
279
+ * without wrapping it in Array, and a user's phone number to the message body:
280
+ */
281
+ await chat.sendMessage(`Hi @${userNumber}`, {
282
+ mentions: userNumber + '@c.us'
283
+ });
284
+ // To mention a list of users:
285
+ await chat.sendMessage(`Hi @${userNumber}, @${userNumber}`, {
286
+ mentions: [userNumber + '@c.us', userNumber + '@c.us']
287
+ });
288
+ } else if (msg.body === '!mentionGroups') {
289
+ const chat = await msg.getChat();
290
+ const groupId = 'YYYYYYYYYY@g.us';
291
+ /**
292
+ * Sends clickable group mentions, the same as user mentions.
293
+ * When the mentions are clicked, it opens a chat with the mentioned group.
294
+ * The 'groupMentions.subject' can be custom
295
+ *
296
+ * @note The user that does not participate in the mentioned group,
297
+ * will not be able to click on that mentioned group, the same if the group does not exist
298
+ *
299
+ * To mention one group:
300
+ */
301
+ await chat.sendMessage(`Check the last message here: @${groupId}`, {
302
+ groupMentions: { subject: 'GroupSubject', id: groupId }
303
+ });
304
+ // To mention a list of groups:
305
+ await chat.sendMessage(`Check the last message in these groups: @${groupId}, @${groupId}`, {
306
+ groupMentions: [
307
+ { subject: 'FirstGroup', id: groupId },
308
+ { subject: 'SecondGroup', id: groupId }
309
+ ]
310
+ });
311
+ } else if (msg.body === '!getGroupMentions') {
312
+ // To get group mentions from a message:
313
+ const groupId = 'ZZZZZZZZZZ@g.us';
314
+ const msg = await client.sendMessage('chatId', `Check the last message here: @${groupId}`, {
315
+ groupMentions: { subject: 'GroupSubject', id: groupId }
316
+ });
317
+ /** {@link groupMentions} is an array of `GroupChat` */
318
+ const groupMentions = await msg.getGroupMentions();
319
+ console.log(groupMentions);
320
+ } else if (msg.body === '!delete') {
321
+ if (msg.hasQuotedMsg) {
322
+ const quotedMsg = await msg.getQuotedMessage();
323
+ if (quotedMsg.fromMe) {
324
+ quotedMsg.delete(true);
325
+ } else {
326
+ msg.reply('I can only delete my own messages');
327
+ }
328
+ }
329
+ } else if (msg.body === '!pin') {
330
+ const chat = await msg.getChat();
331
+ await chat.pin();
332
+ } else if (msg.body === '!archive') {
333
+ const chat = await msg.getChat();
334
+ await chat.archive();
335
+ } else if (msg.body === '!mute') {
336
+ const chat = await msg.getChat();
337
+ // mute the chat for 20 seconds
338
+ const unmuteDate = new Date();
339
+ unmuteDate.setSeconds(unmuteDate.getSeconds() + 20);
340
+ await chat.mute(unmuteDate);
341
+ } else if (msg.body === '!typing') {
342
+ const chat = await msg.getChat();
343
+ // simulates typing in the chat
344
+ chat.sendStateTyping();
345
+ } else if (msg.body === '!recording') {
346
+ const chat = await msg.getChat();
347
+ // simulates recording audio in the chat
348
+ chat.sendStateRecording();
349
+ } else if (msg.body === '!clearstate') {
350
+ const chat = await msg.getChat();
351
+ // stops typing or recording in the chat
352
+ chat.clearState();
353
+ } else if (msg.body === '!jumpto') {
354
+ if (msg.hasQuotedMsg) {
355
+ const quotedMsg = await msg.getQuotedMessage();
356
+ client.interface.openChatWindowAt(quotedMsg.id._serialized);
357
+ }
358
+ } else if (msg.body === '!buttons') {
359
+ let button = new Buttons('Button body', [{ body: 'bt1' }, { body: 'bt2' }, { body: 'bt3' }], 'title', 'footer');
360
+ client.sendMessage(msg.from, button);
361
+ } else if (msg.body === '!list') {
362
+ let sections = [
363
+ { title: 'sectionTitle', rows: [{ title: 'ListItem1', description: 'desc' }, { title: 'ListItem2' }] }
364
+ ];
365
+ let list = new List('List body', 'btnText', sections, 'Title', 'footer');
366
+ client.sendMessage(msg.from, list);
367
+ } else if (msg.body === '!reaction') {
368
+ await msg.react('👍');
369
+ } else if (msg.body === '!sendpoll') {
370
+ /** By default the poll is created as a single choice poll: */
371
+ await msg.reply(new Poll('Winter or Summer?', ['Winter', 'Summer']));
372
+ /** If you want to provide a multiple choice poll, add allowMultipleAnswers as true: */
373
+ await msg.reply(new Poll('Cats or Dogs?', ['Cats', 'Dogs'], { allowMultipleAnswers: true }));
374
+ /**
375
+ * You can provide a custom message secret, it can be used as a poll ID:
376
+ * @note It has to be a unique vector with a length of 32
377
+ */
378
+ await msg.reply(
379
+ new Poll('Cats or Dogs?', ['Cats', 'Dogs'], {
380
+ messageSecret: [
381
+ 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
382
+ ]
383
+ })
384
+ );
385
+ } else if (msg.body === '!vote') {
386
+ if (msg.hasQuotedMsg) {
387
+ const quotedMsg = await msg.getQuotedMessage();
388
+ if (quotedMsg.type === 'poll_creation') {
389
+ await quotedMsg.vote(msg.body.replace('!vote', ''));
390
+ } else {
391
+ msg.reply('Can only be used on poll messages');
392
+ }
393
+ }
394
+ } else if (msg.body === '!edit') {
395
+ if (msg.hasQuotedMsg) {
396
+ const quotedMsg = await msg.getQuotedMessage();
397
+ if (quotedMsg.fromMe) {
398
+ await quotedMsg.edit(msg.body.replace('!edit', ''));
399
+ } else {
400
+ msg.reply('I can only edit my own messages');
401
+ }
402
+ }
403
+ } else if (msg.body === '!updatelabels') {
404
+ const chat = await msg.getChat();
405
+ await chat.changeLabels([0, 1]);
406
+ } else if (msg.body === '!addlabels') {
407
+ const chat = await msg.getChat();
408
+ let labels = (await chat.getLabels()).map((l) => l.id);
409
+ labels.push('0');
410
+ labels.push('1');
411
+ await chat.changeLabels(labels);
412
+ } else if (msg.body === '!removelabels') {
413
+ const chat = await msg.getChat();
414
+ await chat.changeLabels([]);
415
+ } else if (msg.body === '!approverequest') {
416
+ /**
417
+ * Presented an example for membership request approvals, the same examples are for the request rejections.
418
+ * To approve the membership request from a specific user:
419
+ */
420
+ await client.approveGroupMembershipRequests(msg.from, { requesterIds: 'number@c.us' });
421
+ /** The same for execution on group object (no need to provide the group ID): */
422
+ const group = await msg.getChat();
423
+ await group.approveGroupMembershipRequests({ requesterIds: 'number@c.us' });
424
+ /** To approve several membership requests: */
425
+ const approval = await client.approveGroupMembershipRequests(msg.from, {
426
+ requesterIds: ['number1@c.us', 'number2@c.us']
427
+ });
428
+ /**
429
+ * The example of the {@link approval} output:
430
+ * [
431
+ * {
432
+ * requesterId: 'number1@c.us',
433
+ * message: 'Rejected successfully'
434
+ * },
435
+ * {
436
+ * requesterId: 'number2@c.us',
437
+ * error: 404,
438
+ * message: 'ParticipantRequestNotFoundError'
439
+ * }
440
+ * ]
441
+ *
442
+ */
443
+ console.log(approval);
444
+ /** To approve all the existing membership requests (simply don't provide any user IDs): */
445
+ await client.approveGroupMembershipRequests(msg.from);
446
+ /** To change the sleep value to 300 ms: */
447
+ await client.approveGroupMembershipRequests(msg.from, {
448
+ requesterIds: ['number1@c.us', 'number2@c.us'],
449
+ sleep: 300
450
+ });
451
+ /** To change the sleep value to random value between 100 and 300 ms: */
452
+ await client.approveGroupMembershipRequests(msg.from, {
453
+ requesterIds: ['number1@c.us', 'number2@c.us'],
454
+ sleep: [100, 300]
455
+ });
456
+ /** To explicitly disable the sleep: */
457
+ await client.approveGroupMembershipRequests(msg.from, {
458
+ requesterIds: ['number1@c.us', 'number2@c.us'],
459
+ sleep: null
460
+ });
461
+ } else if (msg.body === '!pinmsg') {
462
+ /**
463
+ * Pins a message in a chat, a method takes a number in seconds for the message to be pinned.
464
+ * WhatsApp default values for duration to pass to the method are:
465
+ * 1. 86400 for 24 hours
466
+ * 2. 604800 for 7 days
467
+ * 3. 2592000 for 30 days
468
+ * You can pass your own value:
469
+ */
470
+ const result = await msg.pin(60); // Will pin a message for 1 minute
471
+ console.log(result); // True if the operation completed successfully, false otherwise
472
+ } else if (msg.body === '!howManyConnections') {
473
+ /**
474
+ * Get user device count by ID
475
+ * Each WaWeb Connection counts as one device, and the phone (if exists) counts as one
476
+ * So for a non-enterprise user with one WaWeb connection it should return "2"
477
+ */
478
+ let deviceCount = await client.getContactDeviceCount(msg.from);
479
+ await msg.reply(`You have *${deviceCount}* devices connected`);
480
+ } else if (msg.body === '!syncHistory') {
481
+ const isSynced = await client.syncHistory(msg.from);
482
+ // Or through the Chat object:
483
+ // const chat = await client.getChatById(msg.from);
484
+ // const isSynced = await chat.syncHistory();
485
+
486
+ await msg.reply(isSynced ? 'Historical chat is syncing..' : 'There is no historical chat to sync.');
487
+ } else if (msg.body === '!statuses') {
488
+ const statuses = await client.getBroadcasts();
489
+ console.log(statuses);
490
+ const chat = await statuses[0]?.getChat(); // Get user chat of a first status
491
+ console.log(chat);
492
+ } else if (msg.body === '!sendMediaHD' && msg.hasQuotedMsg) {
493
+ const quotedMsg = await msg.getQuotedMessage();
494
+ if (quotedMsg.hasMedia) {
495
+ const media = await quotedMsg.downloadMedia();
496
+ await client.sendMessage(msg.from, media, { sendMediaAsHd: true });
497
+ }
498
+ } else if (msg.body === '!parseVCard') {
499
+ const vCard =
500
+ 'BEGIN:VCARD\n' +
501
+ 'VERSION:3.0\n' +
502
+ 'FN:John Doe\n' +
503
+ 'ORG:Microsoft;\n' +
504
+ 'EMAIL;type=INTERNET:john.doe@gmail.com\n' +
505
+ 'URL:www.johndoe.com\n' +
506
+ 'TEL;type=CELL;type=VOICE;waid=18006427676:+1 (800) 642 7676\n' +
507
+ 'END:VCARD';
508
+ const vCardExtended =
509
+ 'BEGIN:VCARD\n' +
510
+ 'VERSION:3.0\n' +
511
+ 'FN:John Doe\n' +
512
+ 'ORG:Microsoft;\n' +
513
+ 'item1.TEL:+1 (800) 642 7676\n' +
514
+ 'item1.X-ABLabel:USA Customer Service\n' +
515
+ 'item2.TEL:+55 11 4706 0900\n' +
516
+ 'item2.X-ABLabel:Brazil Customer Service\n' +
517
+ 'PHOTO;BASE64:here you can paste a binary data of a contact photo in Base64 encoding\n' +
518
+ 'END:VCARD';
519
+ const userId = 'XXXXXXXXXX@c.us';
520
+ await client.sendMessage(userId, vCard);
521
+ await client.sendMessage(userId, vCardExtended);
522
+ } else if (msg.body === '!changeSync') {
523
+ // NOTE: this action will take effect after you restart the client.
524
+ const backgroundSync = await client.setBackgroundSync(true);
525
+ console.log(backgroundSync);
526
+ } else if (msg.body === '!postStatus') {
527
+ await client.sendMessage('status@broadcast', 'Hello there!');
528
+ // send with a different style
529
+ await client.sendMessage('status@broadcast', 'Hello again! Looks different?', {
530
+ fontStyle: 1,
531
+ backgroundColor: '#0b3296'
532
+ });
533
+ }
534
+ });
535
+
536
+ client.on('message_create', async (msg) => {
537
+ // Fired on all message creations, including your own
538
+ if (msg.fromMe) {
539
+ // do stuff here
540
+ }
541
+
542
+ // Unpins a message
543
+ if (msg.fromMe && msg.body.startsWith('!unpin')) {
544
+ const pinnedMsg = await msg.getQuotedMessage();
545
+ if (pinnedMsg) {
546
+ // Will unpin a message
547
+ const result = await pinnedMsg.unpin();
548
+ console.log(result); // True if the operation completed successfully, false otherwise
549
+ }
550
+ }
551
+ });
552
+
553
+ client.on('message_ciphertext', (msg) => {
554
+ // Receiving new incoming messages that have been encrypted
555
+ // msg.type === 'ciphertext'
556
+ msg.body = 'Waiting for this message. Check your phone.';
557
+
558
+ // do stuff here
559
+ });
560
+
561
+ client.on('message_revoke_everyone', async (after, before) => {
562
+ // Fired whenever a message is deleted by anyone (including you)
563
+ console.log(after); // message after it was deleted.
564
+ if (before) {
565
+ console.log(before); // message before it was deleted.
566
+ }
567
+ });
568
+
569
+ client.on('message_revoke_me', async (msg) => {
570
+ // Fired whenever a message is only deleted in your own view.
571
+ console.log(msg.body); // message before it was deleted.
572
+ });
573
+
574
+ client.on('message_ack', (msg, ack) => {
575
+ /*
576
+ == ACK VALUES ==
577
+ ACK_ERROR: -1
578
+ ACK_PENDING: 0
579
+ ACK_SERVER: 1
580
+ ACK_DEVICE: 2
581
+ ACK_READ: 3
582
+ ACK_PLAYED: 4
583
+ */
584
+
585
+ if (ack == 3) {
586
+ // The message was read
587
+ }
588
+ });
589
+
590
+ client.on('group_join', (notification) => {
591
+ // User has joined or been added to the group.
592
+ console.log('join', notification);
593
+ notification.reply('User joined.');
594
+ });
595
+
596
+ client.on('group_leave', (notification) => {
597
+ // User has left or been kicked from the group.
598
+ console.log('leave', notification);
599
+ notification.reply('User left.');
600
+ });
601
+
602
+ client.on('group_update', (notification) => {
603
+ // Group picture, subject or description has been updated.
604
+ console.log('update', notification);
605
+ });
606
+
607
+ client.on('change_state', state => {
608
+ console.log('CHANGE STATE', state);
609
+ });
610
+
611
+ // Change to false if you don't want to reject incoming calls
612
+ let rejectCalls = true;
613
+
614
+ client.on('call', async (call) => {
615
+ console.log('Call received, rejecting. GOTO Line 261 to disable', call);
616
+ if (rejectCalls) await call.reject();
617
+ await client.sendMessage(call.from, `[${call.fromMe ? 'Outgoing' : 'Incoming'}] Phone call from ${call.from}, type ${call.isGroup ? 'group' : ''} ${call.isVideo ? 'video' : 'audio'} call. ${rejectCalls ? 'This call was automatically rejected by the script.' : ''}`);
618
+ });
619
+
620
+ client.on('disconnected', (reason) => {
621
+ console.log('Client was logged out', reason);
622
+ });
623
+
624
+ client.on('contact_changed', async (message, oldId, newId, isContact) => {
625
+ /** The time the event occurred. */
626
+ const eventTime = (new Date(message.timestamp * 1000)).toLocaleString();
627
+
628
+ console.log(
629
+ `The contact ${oldId.slice(0, -5)}` +
630
+ `${!isContact ? ' that participates in group ' +
631
+ `${(await client.getChatById(message.to ?? message.from)).name} ` : ' '}` +
632
+ `changed their phone number\nat ${eventTime}.\n` +
633
+ `Their new phone number is ${newId.slice(0, -5)}.\n`);
634
+
635
+ /**
636
+ * Information about the @param {message}:
637
+ *
638
+ * 1. If a notification was emitted due to a group participant changing their phone number:
639
+ * @param {message.author} is a participant's id before the change.
640
+ * @param {message.recipients[0]} is a participant's id after the change (a new one).
641
+ *
642
+ * 1.1 If the contact who changed their number WAS in the current user's contact list at the time of the change:
643
+ * @param {message.to} is a group chat id the event was emitted in.
644
+ * @param {message.from} is a current user's id that got an notification message in the group.
645
+ * Also the @param {message.fromMe} is TRUE.
646
+ *
647
+ * 1.2 Otherwise:
648
+ * @param {message.from} is a group chat id the event was emitted in.
649
+ * @param {message.to} is @type {undefined}.
650
+ * Also @param {message.fromMe} is FALSE.
651
+ *
652
+ * 2. If a notification was emitted due to a contact changing their phone number:
653
+ * @param {message.templateParams} is an array of two user's ids:
654
+ * the old (before the change) and a new one, stored in alphabetical order.
655
+ * @param {message.from} is a current user's id that has a chat with a user,
656
+ * whos phone number was changed.
657
+ * @param {message.to} is a user's id (after the change), the current user has a chat with.
658
+ */
659
+ });
660
+
661
+ client.on('group_admin_changed', (notification) => {
662
+ if (notification.type === 'promote') {
663
+ /**
664
+ * Emitted when a current user is promoted to an admin.
665
+ * {@link notification.author} is a user who performs the action of promoting/demoting the current user.
666
+ */
667
+ console.log(`You were promoted by ${notification.author}`);
668
+ } else if (notification.type === 'demote')
669
+ /** Emitted when a current user is demoted to a regular user. */
670
+ console.log(`You were demoted by ${notification.author}`);
671
+ });
672
+
673
+ client.on('group_membership_request', async (notification) => {
674
+ /**
675
+ * The example of the {@link notification} output:
676
+ * {
677
+ * id: {
678
+ * fromMe: false,
679
+ * remote: 'groupId@g.us',
680
+ * id: '123123123132132132',
681
+ * participant: 'number@c.us',
682
+ * _serialized: 'false_groupId@g.us_123123123132132132_number@c.us'
683
+ * },
684
+ * body: '',
685
+ * type: 'created_membership_requests',
686
+ * timestamp: 1694456538,
687
+ * chatId: 'groupId@g.us',
688
+ * author: 'number@c.us',
689
+ * recipientIds: []
690
+ * }
691
+ *
692
+ */
693
+ console.log(notification);
694
+ /** You can approve or reject the newly appeared membership request: */
695
+ await client.approveGroupMembershipRequestss(notification.chatId, notification.author);
696
+ await client.rejectGroupMembershipRequests(notification.chatId, notification.author);
697
+ });
698
+
699
+ client.on('message_reaction', async (reaction) => {
700
+ console.log('REACTION RECEIVED', reaction);
701
+ });
702
+
703
+ client.on('vote_update', (vote) => {
704
+ /** The vote that was affected: */
705
+ console.log(vote);
706
+ });