@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 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
- ### 8. Message History
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
- ### 8. Message Store (History)
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\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\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: browserConfig,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@innovatorssoft/innovators-bot2",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "WhatsApp API",
5
5
  "main": "index.js",
6
6
  "scripts": {
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
- }