@innovatorssoft/innovators-bot2 2.0.3 ā 2.0.5
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/README.md +103 -2
- package/example.js +223 -2
- package/index.js +160 -6
- package/package.json +1 -1
- package/publish-dual.js +0 -40
package/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
A powerful WhatsApp client library that provides seamless integration between Baileys and WhatsApp-web.js style APIs. This library makes it easy to create WhatsApp bots and automation tools with a familiar interface.
|
|
4
4
|
|
|
5
|
+
## Community
|
|
6
|
+
|
|
7
|
+
> Join our Discord server for support, updates, and discussions:
|
|
8
|
+
> https://discord.gg/G3RfM6FDHS
|
|
9
|
+
|
|
5
10
|
## Features
|
|
6
11
|
|
|
7
12
|
- š Easy to use, familiar WhatsApp-web.js style API
|
|
@@ -91,6 +96,19 @@ await client.sendMessage('1234567890@s.whatsapp.net', {
|
|
|
91
96
|
})
|
|
92
97
|
```
|
|
93
98
|
|
|
99
|
+
### Call Methods
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
// Initiate a voice call
|
|
103
|
+
const { callId } = await client.initiateCall(jid)
|
|
104
|
+
|
|
105
|
+
// Initiate a video call
|
|
106
|
+
const videoCall = await client.initiateCall(jid, { isVideo: true })
|
|
107
|
+
|
|
108
|
+
// Cancel an outgoing call
|
|
109
|
+
await client.cancelCall(callId, jid)
|
|
110
|
+
```
|
|
111
|
+
|
|
94
112
|
### 2. Media Handling
|
|
95
113
|
|
|
96
114
|
```javascript
|
|
@@ -290,6 +308,12 @@ await client.updateGroupsAddPrivacy('contacts')
|
|
|
290
308
|
// Update default disappearing mode for new chats
|
|
291
309
|
// Options: 0 (off), 86400 (24h), 604800 (7d), 7776000 (90d)
|
|
292
310
|
await client.updateDefaultDisappearingMode(604800)
|
|
311
|
+
|
|
312
|
+
// Update profile status
|
|
313
|
+
await client.updateProfileStatus('Hello World!')
|
|
314
|
+
|
|
315
|
+
// Update profile name
|
|
316
|
+
await client.updateProfileName('My name')
|
|
293
317
|
```
|
|
294
318
|
|
|
295
319
|
### 7. Interactive Messages
|
|
@@ -385,10 +409,85 @@ await client.SendList('1234567890@s.whatsapp.net', {
|
|
|
385
409
|
});
|
|
386
410
|
```
|
|
387
411
|
|
|
388
|
-
|
|
412
|
+
#### Interactive Messages (V2)
|
|
413
|
+
|
|
414
|
+
Modern interactive message generation with simplified API.
|
|
415
|
+
|
|
416
|
+
**Quick Reply Buttons (V2)**
|
|
417
|
+
```javascript
|
|
418
|
+
await client.sendQuickReplyV2(jid, 'Please select an option below:', [
|
|
419
|
+
{ id: 'btn-1', displayText: 'ā
Accept' },
|
|
420
|
+
{ id: 'btn-2', displayText: 'ā Reject' }
|
|
421
|
+
], { footer: 'Powered by Innovators Soft' });
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**URL Button (V2)**
|
|
425
|
+
```javascript
|
|
426
|
+
await client.sendUrlButtonV2(jid, 'Visit our website for more info', [
|
|
427
|
+
{ displayText: 'š Open Website', url: 'https://example.com' }
|
|
428
|
+
], { title: 'Product Info', footer: 'Click to open' });
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Copy Code Button (V2)**
|
|
432
|
+
```javascript
|
|
433
|
+
await client.sendCopyCodeV2(jid, 'Your OTP Code is:', '123456', 'š Copy Code');
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**Combined Buttons (Mix URL, Reply, Copy, Call) (V2)**
|
|
437
|
+
```javascript
|
|
438
|
+
await client.sendCombinedButtonsV2(jid, 'Choose an action:', [
|
|
439
|
+
{ type: 'reply', displayText: 'š Order Now', id: 'order' },
|
|
440
|
+
{ type: 'url', displayText: 'š Website', url: 'https://example.com' },
|
|
441
|
+
{ type: 'call', displayText: 'š Phone', phoneNumber: '+923224559543' },
|
|
442
|
+
{ type: 'copy', displayText: 'š Copy Promo', copyCode: 'PROMO2024' }
|
|
443
|
+
], { title: 'Main Menu', footer: 'Innovators Soft' });
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
**List Message (V2)**
|
|
447
|
+
```javascript
|
|
448
|
+
await client.sendListV2(jid, {
|
|
449
|
+
title: 'š Product Menu',
|
|
450
|
+
buttonText: 'View Menu',
|
|
451
|
+
description: 'Please select a product',
|
|
452
|
+
footer: 'Powered by Innovators Soft',
|
|
453
|
+
sections: [
|
|
454
|
+
{
|
|
455
|
+
title: 'Food',
|
|
456
|
+
rows: [
|
|
457
|
+
{ rowId: 'nasi-goreng', title: 'Fried Rice', description: '$2.50' },
|
|
458
|
+
{ rowId: 'mie-goreng', title: 'Fried Noodles', description: '$2.00' }
|
|
459
|
+
]
|
|
460
|
+
}
|
|
461
|
+
]
|
|
462
|
+
});
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### 8. Typing & Presence Control
|
|
466
|
+
|
|
467
|
+
Use `createPresenceController` for manual or standalone typing/recording presence control ā without needing the auto-reply system.
|
|
389
468
|
|
|
390
469
|
```javascript
|
|
391
|
-
|
|
470
|
+
const typing = client.createPresenceController();
|
|
471
|
+
|
|
472
|
+
// Show "typing..." for 2 s, then send the message ā all in one call
|
|
473
|
+
const sent = await typing.simulateTyping(jid, 2000, async () => {
|
|
474
|
+
await client.sendMessage(jid, 'Here is your answer! ā
');
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// Manual start (auto-pauses after 5 s by default)
|
|
478
|
+
await typing.startTyping(jid, { duration: 5000 });
|
|
479
|
+
|
|
480
|
+
// Manual stop
|
|
481
|
+
await typing.stopTyping(jid);
|
|
482
|
+
|
|
483
|
+
// Voice note recording indicator
|
|
484
|
+
await typing.startRecording(jid, { duration: 3000 });
|
|
485
|
+
|
|
486
|
+
// Stop all active indicators (e.g. on socket close)
|
|
487
|
+
await typing.stopAll();
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### 9. Message History (Store)
|
|
392
491
|
|
|
393
492
|
The library includes a robust message store to keep track of chat history, even across reloads.
|
|
394
493
|
|
|
@@ -528,6 +627,8 @@ The library includes example bot commands that you can use:
|
|
|
528
627
|
- `!readreceiptprivacy <value>` - Update read receipts privacy (all/none)
|
|
529
628
|
- `!groupaddprivacy <value>` - Update who can add you to groups (all/contacts/contact_blacklist)
|
|
530
629
|
- `!disappearing <seconds>` - Update default disappearing mode (0/86400/604800/7776000)
|
|
630
|
+
- `!updatestatus <text>` - Update profile status
|
|
631
|
+
- `!updatename <text>` - Update profile name
|
|
531
632
|
|
|
532
633
|
### Interactive Messages
|
|
533
634
|
- `!buttons` - Show interactive buttons
|
package/example.js
CHANGED
|
@@ -9,6 +9,10 @@ const rl = readline.createInterface({
|
|
|
9
9
|
output: process.stdout
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
String.prototype.toTitleCase = function () {
|
|
13
|
+
return this.split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' ');
|
|
14
|
+
};
|
|
15
|
+
|
|
12
16
|
const question = (text) => new Promise((resolve) => rl.question(text, resolve));
|
|
13
17
|
|
|
14
18
|
async function start() {
|
|
@@ -167,6 +171,10 @@ async function start() {
|
|
|
167
171
|
});
|
|
168
172
|
|
|
169
173
|
// Listen for incoming messages
|
|
174
|
+
let lastOutgoingCallId = null;
|
|
175
|
+
let lastOutgoingCallJid = null;
|
|
176
|
+
let autoCancelCallTimer = null;
|
|
177
|
+
|
|
170
178
|
client.on('message', async msg => {
|
|
171
179
|
|
|
172
180
|
if (msg.body === '') {
|
|
@@ -363,6 +371,140 @@ async function start() {
|
|
|
363
371
|
});
|
|
364
372
|
break
|
|
365
373
|
|
|
374
|
+
case '!quickreplyv2':
|
|
375
|
+
await client.sendQuickReplyV2(msg.from, 'Please select an option below:', [
|
|
376
|
+
{ id: 'btn-1', displayText: 'ā
Accept' },
|
|
377
|
+
{ id: 'btn-2', displayText: 'ā Reject' },
|
|
378
|
+
{ id: 'btn-3', displayText: 'š Contact Support' }
|
|
379
|
+
], { footer: 'Powered by Innovators Soft' });
|
|
380
|
+
break
|
|
381
|
+
|
|
382
|
+
case '!urlbuttonv2':
|
|
383
|
+
await client.sendUrlButtonV2(msg.from, 'Visit our website for more info', [
|
|
384
|
+
{ displayText: 'š Open Website', url: 'https://example.com' }
|
|
385
|
+
], { title: 'Product Info', footer: 'Click to open' });
|
|
386
|
+
break
|
|
387
|
+
|
|
388
|
+
case '!copycodev2':
|
|
389
|
+
await client.sendCopyCodeV2(msg.from, 'Your OTP Code is:', '123456', 'š Copy Code');
|
|
390
|
+
break
|
|
391
|
+
|
|
392
|
+
case '!combinedv2':
|
|
393
|
+
await client.sendCombinedButtonsV2(msg.from, 'Choose an action:', [
|
|
394
|
+
{ type: 'reply', displayText: 'š Order Now', id: 'order' },
|
|
395
|
+
{ type: 'url', displayText: 'š Website', url: 'https://example.com' },
|
|
396
|
+
{ type: 'call', displayText: 'š Phone', phoneNumber: '+923224559543' },
|
|
397
|
+
{ type: 'copy', displayText: 'š Copy Promo', copyCode: 'PROMO2024' }
|
|
398
|
+
], { title: 'Main Menu', footer: 'Innovators Soft' });
|
|
399
|
+
break
|
|
400
|
+
|
|
401
|
+
case '!listv2':
|
|
402
|
+
await client.sendListV2(msg.from, {
|
|
403
|
+
title: 'š Product Menu',
|
|
404
|
+
buttonText: 'View Menu',
|
|
405
|
+
description: 'Please select a product',
|
|
406
|
+
footer: 'Powered by Innovators Soft',
|
|
407
|
+
sections: [
|
|
408
|
+
{
|
|
409
|
+
title: 'Food',
|
|
410
|
+
rows: [
|
|
411
|
+
{ rowId: 'nasi-goreng', title: 'Fried Rice', description: '$2.50' },
|
|
412
|
+
{ rowId: 'mie-goreng', title: 'Fried Noodles', description: '$2.00' }
|
|
413
|
+
]
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
title: 'Beverages',
|
|
417
|
+
rows: [
|
|
418
|
+
{ rowId: 'es-teh', title: 'Ice Tea', description: '$0.50' },
|
|
419
|
+
{ rowId: 'kopi', title: 'Coffee', description: '$1.00' }
|
|
420
|
+
]
|
|
421
|
+
}
|
|
422
|
+
]
|
|
423
|
+
});
|
|
424
|
+
break
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
case '!call':
|
|
428
|
+
try {
|
|
429
|
+
if (autoCancelCallTimer) {
|
|
430
|
+
clearTimeout(autoCancelCallTimer);
|
|
431
|
+
autoCancelCallTimer = null;
|
|
432
|
+
}
|
|
433
|
+
const result = await client.initiateCall(msg.from);
|
|
434
|
+
lastOutgoingCallId = result?.callId || null;
|
|
435
|
+
lastOutgoingCallJid = msg.from;
|
|
436
|
+
|
|
437
|
+
await client.sendMessage(msg.from, `Calling... CallId: ${lastOutgoingCallId || 'unknown'}`);
|
|
438
|
+
|
|
439
|
+
if (lastOutgoingCallId) {
|
|
440
|
+
autoCancelCallTimer = setTimeout(async () => {
|
|
441
|
+
try {
|
|
442
|
+
await client.cancelCall(lastOutgoingCallId, lastOutgoingCallJid);
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.error('Error auto-canceling call:', error);
|
|
445
|
+
} finally {
|
|
446
|
+
lastOutgoingCallId = null;
|
|
447
|
+
lastOutgoingCallJid = null;
|
|
448
|
+
autoCancelCallTimer = null;
|
|
449
|
+
}
|
|
450
|
+
}, 10000);
|
|
451
|
+
}
|
|
452
|
+
} catch (error) {
|
|
453
|
+
console.error('Error initiating voice call:', error);
|
|
454
|
+
await client.sendMessage(msg.from, 'Failed to initiate call');
|
|
455
|
+
}
|
|
456
|
+
break
|
|
457
|
+
|
|
458
|
+
case '!videocall':
|
|
459
|
+
try {
|
|
460
|
+
if (autoCancelCallTimer) {
|
|
461
|
+
clearTimeout(autoCancelCallTimer);
|
|
462
|
+
autoCancelCallTimer = null;
|
|
463
|
+
}
|
|
464
|
+
const result = await client.initiateCall(msg.from, { isVideo: true });
|
|
465
|
+
lastOutgoingCallId = result?.callId || null;
|
|
466
|
+
lastOutgoingCallJid = msg.from;
|
|
467
|
+
await client.sendMessage(msg.from, `Video calling... CallId: ${lastOutgoingCallId || 'unknown'}`);
|
|
468
|
+
|
|
469
|
+
if (lastOutgoingCallId) {
|
|
470
|
+
autoCancelCallTimer = setTimeout(async () => {
|
|
471
|
+
try {
|
|
472
|
+
await client.cancelCall(lastOutgoingCallId, lastOutgoingCallJid);
|
|
473
|
+
} catch (error) {
|
|
474
|
+
console.error('Error auto-canceling video call:', error);
|
|
475
|
+
} finally {
|
|
476
|
+
lastOutgoingCallId = null;
|
|
477
|
+
lastOutgoingCallJid = null;
|
|
478
|
+
autoCancelCallTimer = null;
|
|
479
|
+
}
|
|
480
|
+
}, 10000);
|
|
481
|
+
}
|
|
482
|
+
} catch (error) {
|
|
483
|
+
console.error('Error initiating video call:', error);
|
|
484
|
+
await client.sendMessage(msg.from, 'Failed to initiate video call');
|
|
485
|
+
}
|
|
486
|
+
break
|
|
487
|
+
|
|
488
|
+
case '!cancelcall':
|
|
489
|
+
try {
|
|
490
|
+
if (autoCancelCallTimer) {
|
|
491
|
+
clearTimeout(autoCancelCallTimer);
|
|
492
|
+
autoCancelCallTimer = null;
|
|
493
|
+
}
|
|
494
|
+
if (!lastOutgoingCallId) {
|
|
495
|
+
await client.sendMessage(msg.from, 'No outgoing call to cancel');
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
await client.cancelCall(lastOutgoingCallId, msg.from);
|
|
499
|
+
await client.sendMessage(msg.from, `Canceled call: ${lastOutgoingCallId}`);
|
|
500
|
+
lastOutgoingCallId = null;
|
|
501
|
+
lastOutgoingCallJid = null;
|
|
502
|
+
} catch (error) {
|
|
503
|
+
console.error('Error canceling call:', error);
|
|
504
|
+
await client.sendMessage(msg.from, 'Failed to cancel call');
|
|
505
|
+
}
|
|
506
|
+
break
|
|
507
|
+
|
|
366
508
|
case '!help':
|
|
367
509
|
const help = `*š Available Commands List*\n\n` +
|
|
368
510
|
`*š¹ Basic Commands*\n` +
|
|
@@ -416,11 +558,23 @@ async function start() {
|
|
|
416
558
|
`⢠!statusprivacy <value> - Update status privacy\n` +
|
|
417
559
|
`⢠!readreceiptprivacy <value> - Update read receipts\n` +
|
|
418
560
|
`⢠!groupaddprivacy <value> - Who can add to groups\n` +
|
|
419
|
-
`⢠!disappearing <seconds> - Default disappearing mode\n
|
|
561
|
+
`⢠!disappearing <seconds> - Default disappearing mode\n` +
|
|
562
|
+
`⢠!updatestatus <text> - Update profile status\n` +
|
|
563
|
+
`⢠!updatename <text> - Update profile name\n\n` +
|
|
420
564
|
|
|
421
565
|
`*šļø Templates & Buttons*\n` +
|
|
422
566
|
`⢠!buttons - Button template\n` +
|
|
423
|
-
`⢠!list - Scrollable list\n
|
|
567
|
+
`⢠!list - Scrollable list\n` +
|
|
568
|
+
`⢠!quickreplyv2 - Quick reply buttons V2\n` +
|
|
569
|
+
`⢠!urlbuttonv2 - URL button V2\n` +
|
|
570
|
+
`⢠!copycodev2 - Copy code button V2\n` +
|
|
571
|
+
`⢠!combinedv2 - Mixed buttons V2\n` +
|
|
572
|
+
`⢠!listv2 - Interactive list V2\n\n` +
|
|
573
|
+
|
|
574
|
+
`*š Calls*\n` +
|
|
575
|
+
`⢠!call - Initiate a voice call\n` +
|
|
576
|
+
`⢠!videocall - Initiate a video call\n` +
|
|
577
|
+
`⢠!cancelcall - Cancel last outgoing call\n\n` +
|
|
424
578
|
|
|
425
579
|
`*ļæ½ Message Store*\n` +
|
|
426
580
|
`⢠!messages - Get stored messages for this chat\n` +
|
|
@@ -442,6 +596,10 @@ async function start() {
|
|
|
442
596
|
`⢠!typing - Show typing indicator\n` +
|
|
443
597
|
`⢠!recording - Show recording indicator\n` +
|
|
444
598
|
`⢠!paused - Clear typing or recording indicator\n` +
|
|
599
|
+
`⢠!typing_simulate - Simulate typing for 5s then send msg\n` +
|
|
600
|
+
`⢠!typing_start - Start typing with auto-pause\n` +
|
|
601
|
+
`⢠!typing_stop - Stop typing indicator\n` +
|
|
602
|
+
`⢠!recording_start - Start recording indicator\n` +
|
|
445
603
|
`⢠!logout - End session\n\n` +
|
|
446
604
|
|
|
447
605
|
`*š Note*:\nReplace <number> with phone number\n(without + or spaces)`
|
|
@@ -635,6 +793,40 @@ async function start() {
|
|
|
635
793
|
await client.sendMessage(msg.from, 'Stopped typing/recording indicator sent!');
|
|
636
794
|
break;
|
|
637
795
|
|
|
796
|
+
case '!typing_simulate':
|
|
797
|
+
// Show "typing..." for 5 seconds, then send the message ā all in one call
|
|
798
|
+
await client.sendMessage(msg.from, 'Simulating typing for 5 seconds...');
|
|
799
|
+
const typing = client.createPresenceController();
|
|
800
|
+
await typing.simulateTyping(msg.from, 5000, async () => {
|
|
801
|
+
await client.sendMessage(msg.from, 'This message was sent after 5 seconds of typing! ā
');
|
|
802
|
+
});
|
|
803
|
+
break;
|
|
804
|
+
|
|
805
|
+
case '!typing_start':
|
|
806
|
+
// Manual start (auto-pauses after 5 s by default if not specified)
|
|
807
|
+
const typingStart = client.createPresenceController();
|
|
808
|
+
await typingStart.startTyping(msg.from, { duration: 10000 }); // Show for 10s
|
|
809
|
+
await client.sendMessage(msg.from, 'Typing indicator started for 10 seconds.');
|
|
810
|
+
break;
|
|
811
|
+
|
|
812
|
+
case '!typing_stop':
|
|
813
|
+
const typingStop = client.createPresenceController();
|
|
814
|
+
await typingStop.stopTyping(msg.from);
|
|
815
|
+
await client.sendMessage(msg.from, 'Typing indicator stopped.');
|
|
816
|
+
break;
|
|
817
|
+
|
|
818
|
+
case '!recording_start':
|
|
819
|
+
const recordingIndicator = client.createPresenceController();
|
|
820
|
+
await recordingIndicator.startRecording(msg.from, { duration: 5000 });
|
|
821
|
+
await client.sendMessage(msg.from, 'Recording indicator started for 5 seconds.');
|
|
822
|
+
break;
|
|
823
|
+
|
|
824
|
+
case '!typing_stop_all':
|
|
825
|
+
const typingStopAll = client.createPresenceController();
|
|
826
|
+
await typingStopAll.stopAll();
|
|
827
|
+
await client.sendMessage(msg.from, 'All active indicators for this controller stopped.');
|
|
828
|
+
break;
|
|
829
|
+
|
|
638
830
|
case '!read':
|
|
639
831
|
await client.readMessage(msg.raw.key);
|
|
640
832
|
await client.sendMessage(msg.from, 'Message marked as read!');
|
|
@@ -1261,6 +1453,35 @@ async function start() {
|
|
|
1261
1453
|
}
|
|
1262
1454
|
break;
|
|
1263
1455
|
|
|
1456
|
+
case '!updatestatus':
|
|
1457
|
+
try {
|
|
1458
|
+
if (!args) {
|
|
1459
|
+
await client.sendMessage(msg.from, 'ā Please provide a new status.\nUsage: !updatestatus <text>');
|
|
1460
|
+
break;
|
|
1461
|
+
}
|
|
1462
|
+
await client.updateProfileStatus(args.trim());
|
|
1463
|
+
await client.sendMessage(msg.from, `ā
Profile status updated successfully!`);
|
|
1464
|
+
} catch (error) {
|
|
1465
|
+
console.error('Error updating profile status:', error);
|
|
1466
|
+
await client.sendMessage(msg.from, `ā Failed to update profile status: ${error.message}`);
|
|
1467
|
+
}
|
|
1468
|
+
break;
|
|
1469
|
+
|
|
1470
|
+
case '!updatename':
|
|
1471
|
+
try {
|
|
1472
|
+
if (!args) {
|
|
1473
|
+
await client.sendMessage(msg.from, 'ā Please provide a new name.\nUsage: !updatename <text>');
|
|
1474
|
+
break;
|
|
1475
|
+
}
|
|
1476
|
+
const newName = args.trim().toTitleCase();
|
|
1477
|
+
await client.updateProfileName(newName);
|
|
1478
|
+
await client.sendMessage(msg.from, `ā
*${newName}* \nProfile name updated successfully!`);
|
|
1479
|
+
} catch (error) {
|
|
1480
|
+
console.error('Error updating profile name:', error);
|
|
1481
|
+
await client.sendMessage(msg.from, `ā Failed to update profile name: ${error.message}`);
|
|
1482
|
+
}
|
|
1483
|
+
break;
|
|
1484
|
+
|
|
1264
1485
|
case '!messages':
|
|
1265
1486
|
const history = client.getStoredMessages(msg.from);
|
|
1266
1487
|
let historyText = `*š¾ Stored Messages for this chat (${history.length}):*\n\n`;
|
package/index.js
CHANGED
|
@@ -14,7 +14,14 @@ const {
|
|
|
14
14
|
// Anti-Delete
|
|
15
15
|
MessageStore,
|
|
16
16
|
createMessageStoreHandler,
|
|
17
|
-
createAntiDeleteHandler
|
|
17
|
+
createAntiDeleteHandler,
|
|
18
|
+
createTypingIndicator,
|
|
19
|
+
generateInteractiveButtonMessage,
|
|
20
|
+
generateInteractiveListMessage,
|
|
21
|
+
generateCombinedButtons,
|
|
22
|
+
generateCopyCodeButton,
|
|
23
|
+
generateUrlButtonMessage,
|
|
24
|
+
generateQuickReplyButtons
|
|
18
25
|
} = require('@innovatorssoft/baileys');
|
|
19
26
|
|
|
20
27
|
const { Sticker, StickerTypes } = require('wa-sticker-formatter');
|
|
@@ -119,16 +126,15 @@ class WhatsAppClient extends EventEmitter {
|
|
|
119
126
|
|
|
120
127
|
async connect() {
|
|
121
128
|
try {
|
|
129
|
+
if (this._connectionState === 'connecting' && this.sock) {
|
|
130
|
+
return; // Prevent concurrent connection attempts
|
|
131
|
+
}
|
|
122
132
|
|
|
123
133
|
if (this._connectionState !== 'connecting') {
|
|
124
134
|
this._connectionState = 'connecting';
|
|
125
135
|
this.emit('connecting', 'Connecting to WhatsApp...');
|
|
126
136
|
}
|
|
127
137
|
|
|
128
|
-
const browserConfig = this.authmethod === 'pairing'
|
|
129
|
-
? Browsers.iOS('chrome')
|
|
130
|
-
: ["Innovators Soft", "chrome", "1000.26100.275.0"];
|
|
131
|
-
|
|
132
138
|
const { version: baileysVersion, isLatest: baileysIsLatest } = await fetchLatestBaileysVersion();
|
|
133
139
|
const { version: waWebVersion, isLatest: waWebIsLatest } = await fetchLatestWaWebVersion();
|
|
134
140
|
|
|
@@ -147,7 +153,7 @@ class WhatsAppClient extends EventEmitter {
|
|
|
147
153
|
generateHighQualityLinkPreview: true,
|
|
148
154
|
linkPreviewImageThumbnailWidth: 192,
|
|
149
155
|
emitOwnEvents: true,
|
|
150
|
-
browser:
|
|
156
|
+
browser: Browsers.android('Innovators Soft'),
|
|
151
157
|
version: waWebVersion,
|
|
152
158
|
cachedGroupMetadata: async (jid) => {
|
|
153
159
|
const cached = this.groupMetadataCache.get(jid);
|
|
@@ -440,6 +446,7 @@ class WhatsAppClient extends EventEmitter {
|
|
|
440
446
|
|
|
441
447
|
try {
|
|
442
448
|
for (const reaction of reactions) {
|
|
449
|
+
|
|
443
450
|
// Get the chat JID, preferring PN over LID
|
|
444
451
|
let jid = reaction.key.remoteJid;
|
|
445
452
|
if (typeof reaction.key.remoteJidAlt === 'string' &&
|
|
@@ -913,6 +920,80 @@ class WhatsAppClient extends EventEmitter {
|
|
|
913
920
|
throw error;
|
|
914
921
|
}
|
|
915
922
|
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Send Quick Reply Buttons (V2)
|
|
926
|
+
* @param {string} jid - Target JID
|
|
927
|
+
* @param {string} text - Message text
|
|
928
|
+
* @param {Array<object>} buttons - Array of { id, displayText }
|
|
929
|
+
* @param {object} options - { footer }
|
|
930
|
+
*/
|
|
931
|
+
async sendQuickReplyV2(jid, text, buttons, options = {}) {
|
|
932
|
+
if (!this.isConnected) throw new Error('Client is not connected');
|
|
933
|
+
const message = generateQuickReplyButtons(text, buttons, options);
|
|
934
|
+
return await this.sock.sendMessage(jid, message, { ai: true });
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Send Generic Interactive Button Message (V2)
|
|
939
|
+
* @param {string} jid - Target JID
|
|
940
|
+
* @param {object} options - Button options
|
|
941
|
+
*/
|
|
942
|
+
async sendInteractiveButtonV2(jid, options) {
|
|
943
|
+
if (!this.isConnected) throw new Error('Client is not connected');
|
|
944
|
+
const message = generateInteractiveButtonMessage(options);
|
|
945
|
+
return await this.sock.sendMessage(jid, message, { ai: true });
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Send URL Button (V2)
|
|
950
|
+
* @param {string} jid - Target JID
|
|
951
|
+
* @param {string} text - Message text
|
|
952
|
+
* @param {Array<object>} buttons - Array of { displayText, url }
|
|
953
|
+
* @param {object} options - { title, footer }
|
|
954
|
+
*/
|
|
955
|
+
async sendUrlButtonV2(jid, text, buttons, options = {}) {
|
|
956
|
+
if (!this.isConnected) throw new Error('Client is not connected');
|
|
957
|
+
const message = generateUrlButtonMessage(text, buttons, options);
|
|
958
|
+
return await this.sock.sendMessage(jid, message, { ai: true });
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Send Copy Code Button (V2)
|
|
963
|
+
* @param {string} jid - Target JID
|
|
964
|
+
* @param {string} text - Message text
|
|
965
|
+
* @param {string} code - Code to be copied
|
|
966
|
+
* @param {string} buttonText - Text on the copy button
|
|
967
|
+
*/
|
|
968
|
+
async sendCopyCodeV2(jid, text, code, buttonText) {
|
|
969
|
+
if (!this.isConnected) throw new Error('Client is not connected');
|
|
970
|
+
const message = generateCopyCodeButton(text, code, buttonText);
|
|
971
|
+
return await this.sock.sendMessage(jid, message, { ai: true });
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* Send Combined Buttons (V2)
|
|
976
|
+
* @param {string} jid - Target JID
|
|
977
|
+
* @param {string} text - Message text
|
|
978
|
+
* @param {Array<object>} buttons - Mix of { type: 'reply'|'url'|'call'|'copy', ... }
|
|
979
|
+
* @param {object} options - { title, footer }
|
|
980
|
+
*/
|
|
981
|
+
async sendCombinedButtonsV2(jid, text, buttons, options = {}) {
|
|
982
|
+
if (!this.isConnected) throw new Error('Client is not connected');
|
|
983
|
+
const message = generateCombinedButtons(text, buttons, options);
|
|
984
|
+
return await this.sock.sendMessage(jid, message, { ai: true });
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* Send Interactive List Message (V2)
|
|
989
|
+
* @param {string} jid - Target JID
|
|
990
|
+
* @param {object} options - List options (title, buttonText, description, footer, sections)
|
|
991
|
+
*/
|
|
992
|
+
async sendListV2(jid, options) {
|
|
993
|
+
if (!this.isConnected) throw new Error('Client is not connected');
|
|
994
|
+
const message = generateInteractiveListMessage(options);
|
|
995
|
+
return await this.sock.sendMessage(jid, message, { ai: true });
|
|
996
|
+
}
|
|
916
997
|
/**
|
|
917
998
|
* Send an external ad reply with a local image
|
|
918
999
|
* @param {string} number - The phone number to send the ad to
|
|
@@ -1230,6 +1311,32 @@ class WhatsAppClient extends EventEmitter {
|
|
|
1230
1311
|
}
|
|
1231
1312
|
}
|
|
1232
1313
|
|
|
1314
|
+
async initiateCall(jid, options = {}) {
|
|
1315
|
+
if (!this.isConnected) {
|
|
1316
|
+
throw new Error('Client is not connected');
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
try {
|
|
1320
|
+
return await this.sock.initiateCall(jid, options);
|
|
1321
|
+
} catch (error) {
|
|
1322
|
+
console.error('Error initiating call:', error);
|
|
1323
|
+
throw error;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
async cancelCall(callId, jid) {
|
|
1328
|
+
if (!this.isConnected) {
|
|
1329
|
+
throw new Error('Client is not connected');
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
try {
|
|
1333
|
+
return await this.sock.cancelCall(callId, jid);
|
|
1334
|
+
} catch (error) {
|
|
1335
|
+
console.error('Error canceling call:', error);
|
|
1336
|
+
throw error;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1233
1340
|
/**
|
|
1234
1341
|
* Get the underlying socket instance
|
|
1235
1342
|
* @returns {object} The socket instance
|
|
@@ -1268,6 +1375,17 @@ class WhatsAppClient extends EventEmitter {
|
|
|
1268
1375
|
await this.sock.sendPresenceUpdate("paused", jid);
|
|
1269
1376
|
}
|
|
1270
1377
|
|
|
1378
|
+
/**
|
|
1379
|
+
* Create a typing indicator controller for manual or standalone presence control
|
|
1380
|
+
* @returns {object} The typing indicator controller
|
|
1381
|
+
*/
|
|
1382
|
+
createPresenceController() {
|
|
1383
|
+
if (!this.sock) throw new Error('Client is not connected');
|
|
1384
|
+
return createTypingIndicator(
|
|
1385
|
+
(jid, presence) => this.sock.sendPresenceUpdate(presence, jid)
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1271
1389
|
/**
|
|
1272
1390
|
* Reinitialize the WhatsApp client by clearing the session and reconnecting
|
|
1273
1391
|
* @returns {Promise<void>}
|
|
@@ -2348,6 +2466,42 @@ class WhatsAppClient extends EventEmitter {
|
|
|
2348
2466
|
}
|
|
2349
2467
|
}
|
|
2350
2468
|
|
|
2469
|
+
/**
|
|
2470
|
+
* Update profile status (about)
|
|
2471
|
+
* @param {string} status - The new status message
|
|
2472
|
+
* @returns {Promise<void>}
|
|
2473
|
+
* @throws {Error} If client is not connected or update fails
|
|
2474
|
+
*/
|
|
2475
|
+
async updateProfileStatus(status) {
|
|
2476
|
+
if (!this.isConnected) {
|
|
2477
|
+
throw new Error('Client is not connected');
|
|
2478
|
+
}
|
|
2479
|
+
try {
|
|
2480
|
+
await this.sock.updateProfileStatus(status);
|
|
2481
|
+
} catch (error) {
|
|
2482
|
+
console.error('Error updating profile status:', error);
|
|
2483
|
+
throw error;
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
/**
|
|
2488
|
+
* Update profile name
|
|
2489
|
+
* @param {string} name - The new profile name
|
|
2490
|
+
* @returns {Promise<void>}
|
|
2491
|
+
* @throws {Error} If client is not connected or update fails
|
|
2492
|
+
*/
|
|
2493
|
+
async updateProfileName(name) {
|
|
2494
|
+
if (!this.isConnected) {
|
|
2495
|
+
throw new Error('Client is not connected');
|
|
2496
|
+
}
|
|
2497
|
+
try {
|
|
2498
|
+
await this.sock.updateProfileName(name);
|
|
2499
|
+
} catch (error) {
|
|
2500
|
+
console.error('Error updating profile name:', error);
|
|
2501
|
+
throw error;
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2351
2505
|
/**
|
|
2352
2506
|
* Add EXIF metadata to an existing WebP buffer
|
|
2353
2507
|
* @param {Buffer} buffer - WebP buffer
|
package/package.json
CHANGED
package/publish-dual.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const { execSync } = require('child_process');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
const packagePath = path.join(__dirname, 'package.json');
|
|
6
|
-
|
|
7
|
-
// Read the original package.json
|
|
8
|
-
const originalPackage = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
9
|
-
const originalName = originalPackage.name;
|
|
10
|
-
|
|
11
|
-
console.log('š Starting dual npm publish...\n');
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
// Step 1: Publish as unscoped package (innovators-bot2)
|
|
15
|
-
console.log(`š¦ Publishing as "${originalName}"...`);
|
|
16
|
-
execSync('npm publish', { stdio: 'inherit', cwd: __dirname });
|
|
17
|
-
console.log(`ā
Successfully published "${originalName}"\n`);
|
|
18
|
-
|
|
19
|
-
// Step 2: Change name to scoped package
|
|
20
|
-
const scopedName = '@innovatorssoft/innovators-bot2';
|
|
21
|
-
console.log(`š¦ Publishing as "${scopedName}"...`);
|
|
22
|
-
|
|
23
|
-
originalPackage.name = scopedName;
|
|
24
|
-
fs.writeFileSync(packagePath, JSON.stringify(originalPackage, null, 2) + '\n');
|
|
25
|
-
|
|
26
|
-
// Step 3: Publish as scoped package with public access
|
|
27
|
-
execSync('npm publish --access public', { stdio: 'inherit', cwd: __dirname });
|
|
28
|
-
console.log(`ā
Successfully published "${scopedName}"\n`);
|
|
29
|
-
|
|
30
|
-
} catch (error) {
|
|
31
|
-
console.error('ā Error during publish:', error.message);
|
|
32
|
-
process.exitCode = 1;
|
|
33
|
-
} finally {
|
|
34
|
-
// Step 4: Always restore original package.json
|
|
35
|
-
console.log('š Restoring original package.json...');
|
|
36
|
-
originalPackage.name = originalName;
|
|
37
|
-
fs.writeFileSync(packagePath, JSON.stringify(originalPackage, null, 2) + '\n');
|
|
38
|
-
console.log('ā
Original package.json restored');
|
|
39
|
-
console.log('\nš Dual publish complete!');
|
|
40
|
-
}
|