innovators-bot2 2.0.2 → 2.0.4

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
@@ -158,10 +176,20 @@ client.on('message-deleted', async (data) => {
158
176
  ```javascript
159
177
  // Get all groups
160
178
  const groups = await client.getAllGroups()
179
+ groups.forEach(g => console.log(g.subject, g.id, g.notify, g.participants.length))
161
180
 
162
- // Get group metadata (participants, name, description...)
181
+ // Get group metadata (participants, name, description, settings...)
163
182
  const metadata = await client.getGroupMetadata(groupId)
164
- console.log(metadata.id, metadata.subject, metadata.desc)
183
+ console.log(metadata.id, metadata.subject, metadata.desc, metadata.notify)
184
+ console.log('Owner:', metadata.owner, 'Created:', new Date(metadata.creation * 1000))
185
+ console.log('Announce:', metadata.announce, 'Restrict:', metadata.restrict)
186
+
187
+ // Access participants with roles and notify names
188
+ metadata.participants.forEach(p => {
189
+ const role = p.admin === 'superadmin' ? 'Super Admin'
190
+ : p.admin === 'admin' ? 'Admin' : 'Member'
191
+ console.log(`${p.id} - ${role} - Name: ${p.notify || 'N/A'}`)
192
+ })
165
193
 
166
194
  // Create a new group
167
195
  const newGroup = await client.createGroup('Group Name', ['1234567890@s.whatsapp.net'])
@@ -378,11 +406,72 @@ await client.SendList('1234567890@s.whatsapp.net', {
378
406
  ### 8. Message History
379
407
 
380
408
  ```javascript
381
- // Get chat history
382
- const messages = await client.loadMessages(chatId, 50)
409
+ ### 8. Message Store (History)
383
410
 
384
- // Get specific message
385
- const message = await client.loadMessage(chatId, messageId)
411
+ The library includes a robust message store to keep track of chat history, even across reloads.
412
+
413
+ #### Basic Store Operations
414
+ ```javascript
415
+ // Get all stored messages for a specific chat
416
+ const messages = client.getStoredMessages('1234567890@s.whatsapp.net');
417
+
418
+ // Get all stored messages across all chats
419
+ const allMessages = client.getAllStoredMessages();
420
+
421
+ // Get list of all chat JIDs in the store
422
+ const activeChats = client.getStoredChatIds();
423
+
424
+ // Get statistics about the message store
425
+ const stats = client.getStoreStats();
426
+ ```
427
+
428
+ #### Message Store Persistence
429
+
430
+ The message store can be saved to and loaded from a file for consistency across restarts:
431
+
432
+ ```javascript
433
+ // Initialize client with persistence configuration
434
+ const client = new WhatsAppClient({
435
+ sessionName: 'my-session',
436
+ messageStoreFilePath: './data/my-session/message-store.json', // Optional custom path
437
+ autoSaveInterval: 5 * 60 * 1000, // Auto-save every 5 minutes (default)
438
+ maxMessagesPerChat: 1000, // Maximum messages to keep per chat
439
+ messageTTL: 24 * 60 * 60 * 1000 // Message time-to-live (24 hours default)
440
+ });
441
+
442
+ // The store will automatically:
443
+ // - Load from file when connected
444
+ // - Save to file when disconnected
445
+ // - Auto-save periodically (every 5 minutes by default)
446
+
447
+ // Manual save/load operations
448
+ await client.saveMessageStore(); // Returns {success, path, messageCount, savedAt}
449
+ await client.loadMessageStore(); // Returns {success, messageCount, loadedFrom}
450
+ ```
451
+
452
+ **Features:**
453
+ - 💾 **Auto-save**: Automatically saves the store at regular intervals
454
+ - 🔄 **Auto-load**: Loads the store when connecting
455
+ - 🛡️ **Safe shutdown**: Saves the store before disconnecting
456
+ - 📊 **Change tracking**: Only saves when there are new messages
457
+ - 🗂️ **Custom paths**: Configure where to save the store file
458
+
459
+ #### Events
460
+ The message store emits events you can listen to:
461
+ - `message-stored`: Emitted when messages are added to the cache.
462
+ - `store-loaded`: Emitted when the store is loaded from file.
463
+ - `store-cleared`: Emitted when the entire store is cleared.
464
+ - `chat-store-cleared`: Emitted when a specific chat's history is cleared.
465
+
466
+ ```javascript
467
+ client.on('message-stored', (messages) => {
468
+ console.log('Messages cached:', messages.length);
469
+ });
470
+
471
+ client.on('store-loaded', (info) => {
472
+ console.log(`Loaded ${info.messageCount} messages from file`);
473
+ });
474
+ ```
386
475
  ```
387
476
 
388
477
  ## More Examples and Information
@@ -438,7 +527,7 @@ The library includes example bot commands that you can use:
438
527
  - `!revokeinvite` - Revoke current invite code and generate new one
439
528
  - `!leavegroup` - Leave the current group
440
529
  - `!joingroup <code>` - Join a group by invite code
441
- - `!groupinfo <code>` - Get group info by invite code without joining
530
+ - `!groupinfo [jid|code]` - Get full group details (use in-group, by JID, or by invite code) shows participants, roles, names, and group settings
442
531
  - `!joinrequests` - List pending join requests
443
532
  - `!approvejoin <number>` - Approve a join request
444
533
  - `!rejectjoin <number>` - Reject a join request
@@ -463,6 +552,11 @@ The library includes example bot commands that you can use:
463
552
  - `!list` - Display a scrollable list
464
553
  - `!logout` - Logout from current session
465
554
 
555
+ ### 💾 Message Store
556
+ - `!messages` - Get stored messages for current chat
557
+ - `!message <id>` - Get a specific message by ID
558
+ - `!stats` - View store capacity statistics
559
+
466
560
  ### 🛡️ Protection
467
561
  - `Anti-Delete` - Automatically tracks and emits events for deleted messages
468
562
 
@@ -908,6 +1002,8 @@ For more detailed information about the LID system implementation, see:
908
1002
  - [LID_STORE_GUIDE.md](./LID_STORE_GUIDE.md) - Complete implementation guide
909
1003
  - [Baileys Migration Guide](https://baileys.wiki/docs/migration/to-v7.0.0/) - Official migration documentation
910
1004
  - [example.js](./example.js) - Working examples with LID handling
1005
+ - [Read Baileys Documentation](https://innovatorssoftpk.com/)
1006
+ - [Deep Knowlege](https://deepwiki.com/innovatorssoft/Baileys)
911
1007
 
912
1008
  ## Contributing
913
1009
 
@@ -0,0 +1,62 @@
1
+ ## Message Store Persistence Commands
2
+
3
+ The following commands have been added to demonstrate the message store persistence functionality:
4
+
5
+ ### !savestore
6
+ Manually saves the current message store to a JSON file.
7
+ - Creates/updates the file at the configured path (default: `{sessionName}/message-store.json`)
8
+ - Returns statistics about the save operation
9
+
10
+ **Usage:**
11
+ ```
12
+ !savestore
13
+ ```
14
+
15
+ **Example Response:**
16
+ ```
17
+ ✅ Store Saved Successfully
18
+
19
+ • Messages: 2547
20
+ • Path: auth_info_baileys/message-store.json
21
+ • Saved at: 2/17/2026, 11:15:30 AM
22
+ ```
23
+
24
+ ### !loadstore
25
+ Manually loads the message store from the saved JSON file.
26
+ - Clears current in-memory store
27
+ - Loads messages from the file
28
+ - Returns statistics about the load operation
29
+
30
+ **Usage:**
31
+ ```
32
+ !loadstore
33
+ ```
34
+
35
+ **Example Response:**
36
+ ```
37
+ ✅ Store Loaded Successfully
38
+
39
+ • Messages: 2547
40
+ • Loaded from: 2026-02-17T06:10:25.123Z
41
+ ```
42
+
43
+ ## Automatic Persistence
44
+
45
+ The message store automatically:
46
+ - **Loads** when the client connects
47
+ - **Saves** when the client disconnects
48
+ - **Auto-saves** every 5 minutes (configurable) if there are changes
49
+
50
+ ## Configuration
51
+
52
+ Configure persistence in the client initialization:
53
+
54
+ ```javascript
55
+ const client = new WhatsAppClient({
56
+ sessionName: 'my-session',
57
+ messageStoreFilePath: './data/my-session/message-store.json', // Optional
58
+ autoSaveInterval: 5 * 60 * 1000, // 5 minutes (default)
59
+ maxMessagesPerChat: 1000,
60
+ messageTTL: 24 * 60 * 60 * 1000
61
+ });
62
+ ```
package/example.js CHANGED
@@ -43,7 +43,12 @@ async function start() {
43
43
  const client = new WhatsAppClient({
44
44
  sessionName: sessionDir,
45
45
  authmethod: authMethod,
46
- pairingPhoneNumber: pairingPhoneNumber
46
+ pairingPhoneNumber: pairingPhoneNumber,
47
+ // Message store persistence configuration
48
+ messageStoreFilePath: path.join(sessionDir, 'message-store.json'),
49
+ autoSaveInterval: 5 * 60 * 1000, // Auto-save every 5 minutes
50
+ maxMessagesPerChat: 1000, // Keep last 1000 messages per chat
51
+ messageTTL: 24 * 60 * 60 * 1000 // Messages expire after 24 hours
47
52
  });
48
53
 
49
54
  console.log(`\n🚀 Initializing with ${authMethod} method...`);
@@ -94,7 +99,18 @@ async function start() {
94
99
  console.log(`\n🛡️ Message from ${data.jid} was deleted!`)
95
100
  await client.sendMessage(data.jid, '⚠️ I saw you deleted that message! I have it saved in my memory. 😉', { quoted: data.originalMessage });
96
101
  })
102
+ // Example of listening to the new events
103
+ client.on('message-stored', (messages) => {
104
+ //console.log(`${messages.length} messages were just cached in the store.`);
105
+ });
106
+
107
+ client.on('store-loaded', (info) => {
108
+ console.log(`\n💾 Message store loaded: ${info.messageCount} messages restored from file`);
109
+ });
97
110
 
111
+ client.on('store-cleared', () => {
112
+ console.log('Main message store has been purged.');
113
+ });
98
114
  // Handle message reactions
99
115
  client.on('message-reaction', async (reaction) => {
100
116
  console.log('\n👍 Message Reaction Received!')
@@ -151,6 +167,10 @@ async function start() {
151
167
  });
152
168
 
153
169
  // Listen for incoming messages
170
+ let lastOutgoingCallId = null;
171
+ let lastOutgoingCallJid = null;
172
+ let autoCancelCallTimer = null;
173
+
154
174
  client.on('message', async msg => {
155
175
 
156
176
  if (msg.body === '') {
@@ -347,6 +367,87 @@ async function start() {
347
367
  });
348
368
  break
349
369
 
370
+ case '!call':
371
+ try {
372
+ if (autoCancelCallTimer) {
373
+ clearTimeout(autoCancelCallTimer);
374
+ autoCancelCallTimer = null;
375
+ }
376
+ const result = await client.initiateCall(msg.from);
377
+ lastOutgoingCallId = result?.callId || null;
378
+ lastOutgoingCallJid = msg.from;
379
+
380
+ await client.sendMessage(msg.from, `Calling... CallId: ${lastOutgoingCallId || 'unknown'}`);
381
+
382
+ if (lastOutgoingCallId) {
383
+ autoCancelCallTimer = setTimeout(async () => {
384
+ try {
385
+ await client.cancelCall(lastOutgoingCallId, lastOutgoingCallJid);
386
+ } catch (error) {
387
+ console.error('Error auto-canceling call:', error);
388
+ } finally {
389
+ lastOutgoingCallId = null;
390
+ lastOutgoingCallJid = null;
391
+ autoCancelCallTimer = null;
392
+ }
393
+ }, 10000);
394
+ }
395
+ } catch (error) {
396
+ console.error('Error initiating voice call:', error);
397
+ await client.sendMessage(msg.from, 'Failed to initiate call');
398
+ }
399
+ break
400
+
401
+ case '!videocall':
402
+ try {
403
+ if (autoCancelCallTimer) {
404
+ clearTimeout(autoCancelCallTimer);
405
+ autoCancelCallTimer = null;
406
+ }
407
+ const result = await client.initiateCall(msg.from, { isVideo: true });
408
+ lastOutgoingCallId = result?.callId || null;
409
+ lastOutgoingCallJid = msg.from;
410
+ await client.sendMessage(msg.from, `Video calling... CallId: ${lastOutgoingCallId || 'unknown'}`);
411
+
412
+ if (lastOutgoingCallId) {
413
+ autoCancelCallTimer = setTimeout(async () => {
414
+ try {
415
+ await client.cancelCall(lastOutgoingCallId, lastOutgoingCallJid);
416
+ } catch (error) {
417
+ console.error('Error auto-canceling video call:', error);
418
+ } finally {
419
+ lastOutgoingCallId = null;
420
+ lastOutgoingCallJid = null;
421
+ autoCancelCallTimer = null;
422
+ }
423
+ }, 10000);
424
+ }
425
+ } catch (error) {
426
+ console.error('Error initiating video call:', error);
427
+ await client.sendMessage(msg.from, 'Failed to initiate video call');
428
+ }
429
+ break
430
+
431
+ case '!cancelcall':
432
+ try {
433
+ if (autoCancelCallTimer) {
434
+ clearTimeout(autoCancelCallTimer);
435
+ autoCancelCallTimer = null;
436
+ }
437
+ if (!lastOutgoingCallId) {
438
+ await client.sendMessage(msg.from, 'No outgoing call to cancel');
439
+ break;
440
+ }
441
+ await client.cancelCall(lastOutgoingCallId, msg.from);
442
+ await client.sendMessage(msg.from, `Canceled call: ${lastOutgoingCallId}`);
443
+ lastOutgoingCallId = null;
444
+ lastOutgoingCallJid = null;
445
+ } catch (error) {
446
+ console.error('Error canceling call:', error);
447
+ await client.sendMessage(msg.from, 'Failed to cancel call');
448
+ }
449
+ break
450
+
350
451
  case '!help':
351
452
  const help = `*📋 Available Commands List*\n\n` +
352
453
  `*🔹 Basic Commands*\n` +
@@ -382,7 +483,7 @@ async function start() {
382
483
  `• !revokeinvite - Revoke group invite code\n` +
383
484
  `• !leavegroup - Leave the group\n` +
384
485
  `• !joingroup <code> - Join group by invite code\n` +
385
- `• !groupinfo <code> - Get group info by invite code\n` +
486
+ `• !groupinfo [jid|code] - Full group details with participants\n` +
386
487
  `• !joinrequests - List pending join requests\n` +
387
488
  `• !approvejoin <number> - Approve join request\n` +
388
489
  `• !rejectjoin <number> - Reject join request\n` +
@@ -406,7 +507,18 @@ async function start() {
406
507
  `• !buttons - Button template\n` +
407
508
  `• !list - Scrollable list\n\n` +
408
509
 
409
- `*🔐 LID/PN/JID Management*\n` +
510
+ `*📞 Calls*\n` +
511
+ `• !call - Initiate a voice call\n` +
512
+ `• !videocall - Initiate a video call\n` +
513
+ `• !cancelcall - Cancel last outgoing call\n\n` +
514
+
515
+ `*� Message Store*\n` +
516
+ `• !messages - Get stored messages for this chat\n` +
517
+ `• !allmessages - Get statistics for all stored chats\n` +
518
+ `• !message - Get a specific message by ID\n` +
519
+ `• !stats - Get store capacity statistics\n\n` +
520
+
521
+ `*�🔐 LID/PN/JID Management*\n` +
410
522
  `• !lid - Get your LID\n` +
411
523
  `• !pn <lid> - Get PN from LID\n` +
412
524
  `• !parse <jid> - Parse JID info\n` +
@@ -434,6 +546,7 @@ async function start() {
434
546
  groups.forEach((group, index) => {
435
547
  groupList += `${index + 1}. *${group.subject}*\n`
436
548
  groupList += ` ID: ${group.id}\n`
549
+ if (group.notify) groupList += ` Notify: ${group.notify}\n`
437
550
  groupList += ` Members: ${group.participants.length}\n`
438
551
  if (group.desc) groupList += ` Description: ${group.desc}\n`
439
552
  groupList += '\n'
@@ -448,6 +561,45 @@ async function start() {
448
561
  }
449
562
  break
450
563
 
564
+ case '!groupinfo':
565
+ try {
566
+ // Use provided group JID or current group
567
+ const groupJid = args.trim() || msg.raw.key.remoteJid
568
+ if (!groupJid || !groupJid.endsWith('@g.us')) {
569
+ await client.sendMessage(msg.from, '❌ Please provide a group JID or use this command in a group.\nUsage: !groupinfo <groupJid>')
570
+ break
571
+ }
572
+ const groupInfo = await client.getGroupMetadata(groupJid)
573
+ if (groupInfo) {
574
+ let groupList = `*Group Info:*\n\n` +
575
+ `ID: ${groupInfo.id}\n` +
576
+ `Notify: ${groupInfo.notify || 'N/A'}\n` +
577
+ `Subject: ${groupInfo.subject}\n` +
578
+ `Owner: ${groupInfo.owner || 'N/A'}\n` +
579
+ `Created: ${new Date(groupInfo.creation * 1000).toLocaleString()}\n` +
580
+ `Members: ${groupInfo.participants.length}\n` +
581
+ `Description: ${groupInfo.desc || 'N/A'}\n\n` +
582
+ `*👥 Participants:*\n\n`
583
+
584
+ groupInfo.participants.forEach((p, i) => {
585
+ const role = p.admin === 'superadmin' ? '👑 Super Admin'
586
+ : p.admin === 'admin' ? '🛡️ Admin'
587
+ : '👤 Member'
588
+ groupList += `${i + 1}. ${p.id}\n`
589
+ groupList += ` Role: ${role}\n`
590
+ if (p.notify) groupList += ` Name: ${p.notify}\n`
591
+ groupList += '\n'
592
+ })
593
+
594
+ await client.sendMessage(msg.from, groupList)
595
+ } else {
596
+ await client.sendMessage(msg.from, 'Group not found')
597
+ }
598
+ } catch (error) {
599
+ console.error('Error fetching group info:', error)
600
+ await client.sendMessage(msg.from, 'Failed to fetch group info')
601
+ }
602
+ break
451
603
  case '!logout':
452
604
  // Ask for confirmation before logging out
453
605
  await client.sendButtons(msg.from, {
@@ -831,22 +983,65 @@ async function start() {
831
983
 
832
984
  case '!groupinfo':
833
985
  try {
834
- if (!args) {
835
- await client.sendMessage(msg.from, '❌ Please provide an invite code.\nUsage: !groupinfo AbCdEfGhIjK');
836
- break;
986
+ const input = args.trim()
987
+ let groupInfoResult
988
+
989
+ if (!input) {
990
+ // No args: use current group
991
+ if (!msg.raw.key.remoteJid.endsWith('@g.us')) {
992
+ await client.sendMessage(msg.from, '❌ Use this command in a group, or provide a group JID / invite code.\nUsage:\n• !groupinfo (in a group)\n• !groupinfo 120363xxxxx@g.us\n• !groupinfo AbCdEfGhIjK')
993
+ break
994
+ }
995
+ groupInfoResult = await client.getGroupMetadata(msg.raw.key.remoteJid)
996
+ } else if (input.includes('@g.us')) {
997
+ // Argument is a group JID
998
+ groupInfoResult = await client.getGroupMetadata(input)
999
+ } else {
1000
+ // Argument is an invite code (or full link)
1001
+ groupInfoResult = await client.getGroupInfoByInviteCode(input)
837
1002
  }
838
- const groupInfoResult = await client.getGroupInfoByInviteCode(args.trim());
839
- let infoText = `*📋 Group Info*\n\n`;
840
- infoText += `Name: *${groupInfoResult.subject || 'N/A'}*\n`;
841
- infoText += `ID: ${groupInfoResult.id || 'N/A'}\n`;
842
- infoText += `Creator: ${groupInfoResult.owner || 'N/A'}\n`;
843
- infoText += `Description: ${groupInfoResult.desc || 'No description'}\n`;
844
- infoText += `Members: ${groupInfoResult.size || groupInfoResult.participants?.length || 'N/A'}\n`;
845
- infoText += `Created: ${groupInfoResult.creation ? new Date(groupInfoResult.creation * 1000).toLocaleString() : 'N/A'}`;
846
- await client.sendMessage(msg.from, infoText);
1003
+
1004
+ if (!groupInfoResult) {
1005
+ await client.sendMessage(msg.from, '❌ Group not found.')
1006
+ break
1007
+ }
1008
+
1009
+ // Build group info header
1010
+ let infoText = `*📋 Group Info*\n\n`
1011
+ infoText += `*Name:* ${groupInfoResult.subject || 'N/A'}\n`
1012
+ infoText += `*ID:* ${groupInfoResult.id || 'N/A'}\n`
1013
+ if (groupInfoResult.notify) infoText += `*Notify:* ${groupInfoResult.notify}\n`
1014
+ infoText += `*Owner:* ${groupInfoResult.owner || 'N/A'}\n`
1015
+ infoText += `*Created:* ${groupInfoResult.creation ? new Date(groupInfoResult.creation * 1000).toLocaleString() : 'N/A'}\n`
1016
+ infoText += `*Size:* ${groupInfoResult.size || groupInfoResult.participants?.length || 'N/A'}\n`
1017
+ infoText += `*Description:* ${groupInfoResult.desc || 'No description'}\n`
1018
+ if (groupInfoResult.announce !== undefined) infoText += `*Announce:* ${groupInfoResult.announce ? 'Yes (admins only)' : 'No'}\n`
1019
+ if (groupInfoResult.restrict !== undefined) infoText += `*Restricted:* ${groupInfoResult.restrict ? 'Yes (admins only edit info)' : 'No'}\n`
1020
+ if (groupInfoResult.ephemeralDuration) infoText += `*Disappearing:* ${groupInfoResult.ephemeralDuration}s\n`
1021
+ if (groupInfoResult.memberAddMode !== undefined) infoText += `*Member Add:* ${groupInfoResult.memberAddMode ? 'All members' : 'Admins only'}\n`
1022
+ if (groupInfoResult.isCommunity) infoText += `*Community:* Yes\n`
1023
+ if (groupInfoResult.linkedParent) infoText += `*Linked Parent:* ${groupInfoResult.linkedParent}\n`
1024
+
1025
+ // Build participants list if available
1026
+ if (groupInfoResult.participants && groupInfoResult.participants.length > 0) {
1027
+ infoText += `\n*👥 Participants (${groupInfoResult.participants.length}):*\n\n`
1028
+ groupInfoResult.participants.forEach((p, i) => {
1029
+ const role = p.admin === 'superadmin' ? '👑 Super Admin'
1030
+ : p.admin === 'admin' ? '🛡️ Admin'
1031
+ : '👤 Member'
1032
+ infoText += `${i + 1}. ${p.id}\n`
1033
+ infoText += ` Role: ${role}\n`
1034
+ if (p.notify) infoText += ` Name: ${p.notify}\n`
1035
+ if (p.lid) infoText += ` LID: ${p.lid}\n`
1036
+ if (p.phoneNumber) infoText += ` Phone: ${p.phoneNumber}\n`
1037
+ infoText += '\n'
1038
+ })
1039
+ }
1040
+
1041
+ await client.sendMessage(msg.from, infoText)
847
1042
  } catch (error) {
848
- console.error('Error getting group info:', error);
849
- await client.sendMessage(msg.from, `❌ Failed to get group info: ${error.message}`);
1043
+ console.error('Error getting group info:', error)
1044
+ await client.sendMessage(msg.from, `❌ Failed to get group info: ${error.message}`)
850
1045
  }
851
1046
  break;
852
1047
 
@@ -1155,6 +1350,72 @@ async function start() {
1155
1350
  await client.sendMessage(msg.from, `❌ Failed to update: ${error.message}`);
1156
1351
  }
1157
1352
  break;
1353
+
1354
+ case '!messages':
1355
+ const history = client.getStoredMessages(msg.from);
1356
+ let historyText = `*💾 Stored Messages for this chat (${history.length}):*\n\n`;
1357
+ history.slice(-10).forEach((m, i) => {
1358
+ const content = m.message.conversation || m.message.extendedTextMessage?.text || "[Media/Other]";
1359
+ historyText += `${i + 1}. ID: ${m.key.id}\n Text: ${content.substring(0, 50)}${content.length > 50 ? '...' : ''}\n\n`;
1360
+ });
1361
+ await client.sendMessage(msg.from, historyText);
1362
+ break;
1363
+
1364
+ case '!message':
1365
+ if (!args) {
1366
+ await client.sendMessage(msg.from, "❌ Please provide a message ID.");
1367
+ break;
1368
+ }
1369
+ const storedMsg = client.getStoredMessage({ remoteJid: msg.from, id: args.trim(), fromMe: false });
1370
+ if (storedMsg) {
1371
+ await client.sendMessage(msg.from, `✅ Found message!\n\nContent: ${JSON.stringify(storedMsg.message, null, 2).substring(0, 1000)}`);
1372
+ } else {
1373
+ await client.sendMessage(msg.from, "❌ Message not found in store.");
1374
+ }
1375
+ break;
1376
+
1377
+ case '!stats':
1378
+ const stats = client.getStoreStats();
1379
+ await client.sendMessage(msg.from, `*📊 Message Store Statistics*\n\n• Total Chats: ${stats.totalChats}\n• Total Messages: ${stats.totalMessages}\n• Total Deleted: ${stats.totalDeleted}`);
1380
+ break;
1381
+
1382
+ case '!allmessages':
1383
+ const allMsgs = client.getAllStoredMessages();
1384
+ const activeChats = client.getStoredChatIds();
1385
+ await client.sendMessage(msg.from, `*🌐 Global Message Store*\n\n• Total Messages Held: ${allMsgs.length}\n• Total Active Chats: ${activeChats.length}\n\n*Active JIDs:* \n${activeChats.join('\n')}`);
1386
+ break;
1387
+
1388
+ case '!savestore':
1389
+ await client.sendMessage(msg.from, '💾 Saving message store to file...');
1390
+ const saveResult = await client.saveMessageStore();
1391
+ if (saveResult.success) {
1392
+ await client.sendMessage(msg.from,
1393
+ `✅ *Store Saved Successfully*\n\n` +
1394
+ `• Messages: ${saveResult.messageCount}\n` +
1395
+ `• Path: ${saveResult.path}\n` +
1396
+ `• Saved at: ${saveResult.savedAt.toLocaleString()}`
1397
+ );
1398
+ } else {
1399
+ await client.sendMessage(msg.from, `❌ Failed to save store: ${saveResult.error}`);
1400
+ }
1401
+ break;
1402
+
1403
+ case '!loadstore':
1404
+ await client.sendMessage(msg.from, '📂 Loading message store from file...');
1405
+ const loadResult = await client.loadMessageStore();
1406
+ if (loadResult.success) {
1407
+ await client.sendMessage(msg.from,
1408
+ `✅ *Store Loaded Successfully*\n\n` +
1409
+ `• Messages: ${loadResult.messageCount}\n` +
1410
+ `• Loaded from: ${loadResult.loadedFrom}`
1411
+ );
1412
+ } else {
1413
+ await client.sendMessage(msg.from,
1414
+ `⚠️ Could not load store\n` +
1415
+ `Reason: ${loadResult.reason || loadResult.error}`
1416
+ );
1417
+ }
1418
+ break;
1158
1419
  }
1159
1420
  })
1160
1421
 
package/index.js CHANGED
@@ -38,6 +38,7 @@ class Group {
38
38
  constructor(client, groupData) {
39
39
  this.client = client
40
40
  this.id = groupData.id
41
+ this.notify = groupData.notify
41
42
  this.subject = groupData.subject
42
43
  this.creation = groupData.creation
43
44
  this.owner = groupData.owner
@@ -63,6 +64,14 @@ class WhatsAppClient extends EventEmitter {
63
64
  });
64
65
  // Initialize contacts cache
65
66
  this.contactsCache = new NodeCache({ stdTTL: 0, checkperiod: 20 }); // No TTL, manual cleanup on logout
67
+
68
+ // Message store persistence configuration
69
+ this.messageStoreFilePath = config.messageStoreFilePath || path.join(this.sessionName, 'message-store.json');
70
+ this.autoSaveInterval = config.autoSaveInterval || 5 * 60 * 1000; // Default: 5 minutes
71
+ this._autoSaveTimer = null;
72
+ this._storeChangeCount = 0;
73
+ this._pairingCodeTimer = null;
74
+ this._lastStoreSave = null;
66
75
  }
67
76
 
68
77
  /**
@@ -117,7 +126,7 @@ class WhatsAppClient extends EventEmitter {
117
126
  }
118
127
 
119
128
  const browserConfig = this.authmethod === 'pairing'
120
- ? Browsers.windows('chrome')
129
+ ? Browsers.iOS('chrome')
121
130
  : ["Innovators Soft", "chrome", "1000.26100.275.0"];
122
131
 
123
132
  const { version: baileysVersion, isLatest: baileysIsLatest } = await fetchLatestBaileysVersion();
@@ -134,6 +143,7 @@ class WhatsAppClient extends EventEmitter {
134
143
  logger,
135
144
  markOnlineOnConnect: false,
136
145
  syncFullHistory: true,
146
+ getMessage: async (key) => (this.messageStore.getOriginalMessage(key))?.message,
137
147
  generateHighQualityLinkPreview: true,
138
148
  linkPreviewImageThumbnailWidth: 192,
139
149
  emitOwnEvents: true,
@@ -157,8 +167,11 @@ class WhatsAppClient extends EventEmitter {
157
167
 
158
168
  this.store = this.sock.signalRepository.lidMapping;
159
169
 
160
- this.sock.ev.on('connection.update', async (update) => {
161
- const { connection, lastDisconnect, qr } = update;
170
+ this.sock.ev.on('connection.update', async ({ connection, lastDisconnect, qr }) => {
171
+ if (connection === 'close' && this._pairingCodeTimer) {
172
+ clearTimeout(this._pairingCodeTimer);
173
+ this._pairingCodeTimer = null;
174
+ }
162
175
 
163
176
  if (qr && this.authmethod === 'qr') {
164
177
  this.emit('qr', qr);
@@ -176,6 +189,12 @@ class WhatsAppClient extends EventEmitter {
176
189
  };
177
190
  this._connectionState = 'connected';
178
191
  this.emit('connected', userInfo);
192
+
193
+ // Load message store from file
194
+ await this.loadMessageStore();
195
+
196
+ // Start auto-save
197
+ this._startAutoSave();
179
198
  }
180
199
  }
181
200
  }
@@ -186,6 +205,13 @@ class WhatsAppClient extends EventEmitter {
186
205
  if (this._connectionState !== 'disconnected') {
187
206
  this.isConnected = false;
188
207
  this._connectionState = 'disconnected';
208
+
209
+ // Save message store before disconnecting
210
+ await this.saveMessageStore();
211
+
212
+ // Stop auto-save
213
+ this._stopAutoSave();
214
+
189
215
  this.emit('disconnected', lastDisconnect?.error);
190
216
  }
191
217
 
@@ -203,9 +229,20 @@ class WhatsAppClient extends EventEmitter {
203
229
  if (phoneNumber) {
204
230
  try {
205
231
  // Wait a bit for the connection to initialize properly
206
- setTimeout(async () => {
232
+ if (this._pairingCodeTimer) {
233
+ clearTimeout(this._pairingCodeTimer);
234
+ }
235
+
236
+ this._pairingCodeTimer = setTimeout(async () => {
207
237
  const customeCode = "INOVATOR";
208
238
  try {
239
+ if (!this.sock || this._connectionState === 'disconnected') {
240
+ const err = new Error('Socket is not available to request pairing code');
241
+ console.error('Error requesting pairing code:', err);
242
+ this.emit('error', err);
243
+ return;
244
+ }
245
+
209
246
  const code = await this.sock.requestPairingCode(phoneNumber, customeCode);
210
247
  if (code) {
211
248
  // Emit pairing code event so clients can handle it
@@ -216,6 +253,8 @@ class WhatsAppClient extends EventEmitter {
216
253
  } catch (error) {
217
254
  console.error('Error requesting pairing code:', error);
218
255
  this.emit('error', error);
256
+ } finally {
257
+ this._pairingCodeTimer = null;
219
258
  }
220
259
  }, 2000); // Wait 2 seconds before requesting pairing code
221
260
  } catch (error) {
@@ -234,7 +273,17 @@ class WhatsAppClient extends EventEmitter {
234
273
 
235
274
  this.sock.ev.on('messages.upsert', async (update) => {
236
275
  // 📝 Store message for the Anti-Delete system
237
- createMessageStoreHandler(this.messageStore)(update);
276
+ const storeHandler = createMessageStoreHandler(this.messageStore);
277
+ storeHandler(update);
278
+
279
+ // 📢 Emit event that messages were stored
280
+ this.emit('message-stored', update.messages);
281
+
282
+ // Increment change counter for auto-save
283
+ this._incrementStoreChangeCount();
284
+
285
+ // Save message store to file immediately
286
+ await this.saveMessageStore();
238
287
 
239
288
  try {
240
289
  if (update.type !== 'notify' || !update.messages?.length) return;
@@ -1181,6 +1230,32 @@ class WhatsAppClient extends EventEmitter {
1181
1230
  }
1182
1231
  }
1183
1232
 
1233
+ async initiateCall(jid, options = {}) {
1234
+ if (!this.isConnected) {
1235
+ throw new Error('Client is not connected');
1236
+ }
1237
+
1238
+ try {
1239
+ return await this.sock.initiateCall(jid, options);
1240
+ } catch (error) {
1241
+ console.error('Error initiating call:', error);
1242
+ throw error;
1243
+ }
1244
+ }
1245
+
1246
+ async cancelCall(callId, jid) {
1247
+ if (!this.isConnected) {
1248
+ throw new Error('Client is not connected');
1249
+ }
1250
+
1251
+ try {
1252
+ return await this.sock.cancelCall(callId, jid);
1253
+ } catch (error) {
1254
+ console.error('Error canceling call:', error);
1255
+ throw error;
1256
+ }
1257
+ }
1258
+
1184
1259
  /**
1185
1260
  * Get the underlying socket instance
1186
1261
  * @returns {object} The socket instance
@@ -1535,6 +1610,254 @@ class WhatsAppClient extends EventEmitter {
1535
1610
  }
1536
1611
  }
1537
1612
 
1613
+ /**
1614
+ * Get all stored messages for a specific chat
1615
+ * @param {string} chatId - The JID of the chat
1616
+ * @returns {Array} Array of stored messages (WebMessageInfo)
1617
+ */
1618
+ getStoredMessages(chatId) {
1619
+ return this.messageStore.getChatMessages(chatId);
1620
+ }
1621
+
1622
+ /**
1623
+ * Get a specific stored message by its key
1624
+ * @param {object} key - The message key { remoteJid, id, fromMe }
1625
+ * @returns {object|undefined} The stored message (WebMessageInfo)
1626
+ */
1627
+ getStoredMessage(key) {
1628
+ return this.messageStore.getOriginalMessage(key);
1629
+ }
1630
+
1631
+ /**
1632
+ * Get statistics about the message store
1633
+ * @returns {object} Store statistics
1634
+ */
1635
+ getStoreStats() {
1636
+ return this.messageStore.getStats();
1637
+ }
1638
+
1639
+ /**
1640
+ * Get all stored messages from all chats
1641
+ * @returns {Array} Array of all stored messages
1642
+ */
1643
+ getAllStoredMessages() {
1644
+ return this.messageStore.getChatIds().flatMap(chatId => this.messageStore.getChatMessages(chatId));
1645
+ }
1646
+
1647
+ /**
1648
+ * Get IDs of all chats that have stored messages
1649
+ * @returns {Array<string>} Array of chat JIDs
1650
+ */
1651
+ getStoredChatIds() {
1652
+ return this.messageStore.getChatIds();
1653
+ }
1654
+
1655
+ /**
1656
+ * Clear the entire message store
1657
+ */
1658
+ clearMessageStore() {
1659
+ this.messageStore.clear();
1660
+ this.emit('store-cleared');
1661
+ }
1662
+
1663
+ /**
1664
+ * Clear stored messages for a specific chat
1665
+ * @param {string} chatId - The JID of the chat to clear
1666
+ */
1667
+ clearChatStore(chatId) {
1668
+ this.messageStore.clearChat(chatId);
1669
+ this.emit('chat-store-cleared', chatId);
1670
+ }
1671
+
1672
+ /**
1673
+ * Save the message store to a JSON file
1674
+ * @returns {Promise<{success: boolean, path: string, messageCount: number}>}
1675
+ */
1676
+ async saveMessageStore() {
1677
+ try {
1678
+ const rawAllMessages = this.messageStore.getAllMessages();
1679
+ const stats = this.messageStore.getStats();
1680
+
1681
+ let allMessages = [];
1682
+ if (Array.isArray(rawAllMessages)) {
1683
+ allMessages = rawAllMessages;
1684
+ } else if (rawAllMessages && typeof rawAllMessages[Symbol.iterator] === 'function') {
1685
+ allMessages = Array.from(rawAllMessages);
1686
+ } else if (rawAllMessages && typeof rawAllMessages === 'object') {
1687
+ allMessages = Object.values(rawAllMessages);
1688
+ } else {
1689
+ try {
1690
+ allMessages = this.messageStore.getChatIds().flatMap(chatId => this.messageStore.getChatMessages(chatId));
1691
+ } catch {
1692
+ allMessages = [];
1693
+ }
1694
+ }
1695
+
1696
+ // Serialize the store data
1697
+ const storeData = {
1698
+ version: '1.0.0',
1699
+ savedAt: new Date().toISOString(),
1700
+ sessionName: this.sessionName,
1701
+ stats: stats,
1702
+ messages: []
1703
+ };
1704
+
1705
+ // Convert Map structure to serializable array format
1706
+ for (const msg of allMessages) {
1707
+ try {
1708
+ storeData.messages.push({
1709
+ key: msg.key,
1710
+ message: msg.message,
1711
+ messageTimestamp: msg.messageTimestamp,
1712
+ pushName: msg.pushName,
1713
+ broadcast: msg.broadcast,
1714
+ isDeleted: msg.isDeleted
1715
+ });
1716
+ } catch (err) {
1717
+ console.error('Error serializing message:', err);
1718
+ }
1719
+ }
1720
+
1721
+ // Ensure directory exists
1722
+ const dir = path.dirname(this.messageStoreFilePath);
1723
+ if (!fs.existsSync(dir)) {
1724
+ fs.mkdirSync(dir, { recursive: true });
1725
+ }
1726
+
1727
+ // Write to file
1728
+ fs.writeFileSync(
1729
+ this.messageStoreFilePath,
1730
+ JSON.stringify(storeData, null, 2),
1731
+ 'utf8'
1732
+ );
1733
+
1734
+ this._lastStoreSave = new Date();
1735
+ this._storeChangeCount = 0;
1736
+
1737
+ // console.log(`[MessageStore] Saved ${storeData.messages.length} messages to ${this.messageStoreFilePath}`);
1738
+
1739
+ return {
1740
+ success: true,
1741
+ path: this.messageStoreFilePath,
1742
+ messageCount: storeData.messages.length,
1743
+ savedAt: this._lastStoreSave
1744
+ };
1745
+ } catch (error) {
1746
+ console.error('[MessageStore] Error saving store:', error);
1747
+ return {
1748
+ success: false,
1749
+ error: error.message
1750
+ };
1751
+ }
1752
+ }
1753
+
1754
+ /**
1755
+ * Load the message store from a JSON file
1756
+ * @returns {Promise<{success: boolean, messageCount: number}>}
1757
+ */
1758
+ async loadMessageStore() {
1759
+ try {
1760
+ if (!fs.existsSync(this.messageStoreFilePath)) {
1761
+ // console.log('[MessageStore] No saved store file found');
1762
+ return { success: false, reason: 'file_not_found' };
1763
+ }
1764
+
1765
+ const fileContent = fs.readFileSync(this.messageStoreFilePath, 'utf8');
1766
+ const storeData = JSON.parse(fileContent);
1767
+
1768
+ // Validate data structure
1769
+ if (!storeData.messages || !Array.isArray(storeData.messages)) {
1770
+ throw new Error('Invalid store file format');
1771
+ }
1772
+
1773
+ // Clear existing store
1774
+ this.messageStore.clear();
1775
+
1776
+ // Load messages into the store
1777
+ let loadedCount = 0;
1778
+ for (const msgData of storeData.messages) {
1779
+ try {
1780
+ // Use the message store's internal handler to properly store messages
1781
+ const storeHandler = createMessageStoreHandler(this.messageStore);
1782
+ storeHandler({
1783
+ messages: [{
1784
+ key: msgData.key,
1785
+ message: msgData.message,
1786
+ messageTimestamp: msgData.messageTimestamp,
1787
+ pushName: msgData.pushName,
1788
+ broadcast: msgData.broadcast
1789
+ }],
1790
+ type: 'append'
1791
+ });
1792
+ loadedCount++;
1793
+ } catch (err) {
1794
+ console.error('[MessageStore] Error loading message:', err);
1795
+ }
1796
+ }
1797
+
1798
+ this._lastStoreSave = new Date(storeData.savedAt);
1799
+ this._storeChangeCount = 0;
1800
+
1801
+ // console.log(`[MessageStore] Loaded ${loadedCount}/${storeData.messages.length} messages from ${this.messageStoreFilePath}`);
1802
+
1803
+ this.emit('store-loaded', { messageCount: loadedCount });
1804
+
1805
+ return {
1806
+ success: true,
1807
+ messageCount: loadedCount,
1808
+ loadedFrom: storeData.savedAt
1809
+ };
1810
+ } catch (error) {
1811
+ console.error('[MessageStore] Error loading store:', error);
1812
+ return {
1813
+ success: false,
1814
+ error: error.message
1815
+ };
1816
+ }
1817
+ }
1818
+
1819
+ /**
1820
+ * Start auto-save timer
1821
+ * @private
1822
+ */
1823
+ _startAutoSave() {
1824
+ if (this._autoSaveTimer) {
1825
+ clearInterval(this._autoSaveTimer);
1826
+ }
1827
+
1828
+ if (this.autoSaveInterval > 0) {
1829
+ this._autoSaveTimer = setInterval(async () => {
1830
+ // Only save if there have been changes
1831
+ if (this._storeChangeCount > 0) {
1832
+ // console.log(`[MessageStore] Auto-saving (${this._storeChangeCount} changes since last save)`);
1833
+ await this.saveMessageStore();
1834
+ }
1835
+ }, this.autoSaveInterval);
1836
+
1837
+ // console.log(`[MessageStore] Auto-save enabled (interval: ${this.autoSaveInterval}ms)`);
1838
+ }
1839
+ }
1840
+
1841
+ /**
1842
+ * Stop auto-save timer
1843
+ * @private
1844
+ */
1845
+ _stopAutoSave() {
1846
+ if (this._autoSaveTimer) {
1847
+ clearInterval(this._autoSaveTimer);
1848
+ this._autoSaveTimer = null;
1849
+ // console.log('[MessageStore] Auto-save disabled');
1850
+ }
1851
+ }
1852
+
1853
+ /**
1854
+ * Increment the store change counter (called when messages are added)
1855
+ * @private
1856
+ */
1857
+ _incrementStoreChangeCount() {
1858
+ this._storeChangeCount++;
1859
+ }
1860
+
1538
1861
  // ═══════════════════════════════════════════════════════════
1539
1862
  // 📁 GROUP MANAGEMENT METHODS
1540
1863
  // ═══════════════════════════════════════════════════════════
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "innovators-bot2",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "WhatsApp API",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "homepage": "https://github.com/innovatorssoft/WhatsAppAPI?tab=readme-ov-file#whatsapp-api",
32
32
  "dependencies": {
33
- "@innovatorssoft/baileys": "^7.4.0",
33
+ "@innovatorssoft/baileys": "latest",
34
34
  "figlet": "^1.8.0",
35
35
  "mime": "^3.0.0",
36
36
  "mime-types": "^2.1.35",
@@ -39,4 +39,4 @@
39
39
  "qrcode-terminal": "^0.12.0",
40
40
  "wa-sticker-formatter": "^4.4.4"
41
41
  }
42
- }
42
+ }