innovators-bot2 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,12 +9,14 @@ A powerful WhatsApp client library that provides seamless integration between Ba
9
9
  - 💬 Send and receive messages
10
10
  - � Message reactions (add/remove emoji reactions)
11
11
  - �📸 Media handling (images, videos, documents)
12
- - 👥 Group management
12
+ - Mentions support in text and media messages
13
+ - �👥 Group management
13
14
  - 💾 Message history and chat management
14
15
  - 🔄 Auto-reconnect functionality
15
16
  - 📝 Read receipts
16
17
  - 🔐 LID (Local Identifier) support for enhanced privacy
17
18
  - 🗂️ Signal repository store for LID/PN mapping
19
+ - 🧩 Interactive buttons support for both text and media (URL or local file)
18
20
 
19
21
  ## Installation
20
22
 
@@ -97,10 +99,22 @@ await client.sendMedia('1234567890@s.whatsapp.net', './image.jpg', {
97
99
  caption: 'Check out this image!'
98
100
  })
99
101
 
102
+ // Send an image with mentions
103
+ await client.sendMedia('1234567890@s.whatsapp.net', './image.jpg', {
104
+ caption: 'Hey @user, check this out!',
105
+ mentions: ['user@s.whatsapp.net']
106
+ })
107
+
100
108
  // Send a document
101
109
  await client.sendDocument('1234567890@s.whatsapp.net', './document.pdf',
102
110
  'Check out this document!'
103
111
  )
112
+
113
+ // Send a document with mentions (caption object is supported)
114
+ await client.sendDocument('1234567890@s.whatsapp.net', './document.pdf', {
115
+ caption: 'Hey @user, please read this',
116
+ mentions: ['user@s.whatsapp.net']
117
+ })
104
118
  ```
105
119
 
106
120
  ### 3. Sticker Management
@@ -144,10 +158,20 @@ client.on('message-deleted', async (data) => {
144
158
  ```javascript
145
159
  // Get all groups
146
160
  const groups = await client.getAllGroups()
161
+ groups.forEach(g => console.log(g.subject, g.id, g.notify, g.participants.length))
147
162
 
148
- // Get group metadata (participants, name, description...)
163
+ // Get group metadata (participants, name, description, settings...)
149
164
  const metadata = await client.getGroupMetadata(groupId)
150
- console.log(metadata.id, metadata.subject, metadata.desc)
165
+ console.log(metadata.id, metadata.subject, metadata.desc, metadata.notify)
166
+ console.log('Owner:', metadata.owner, 'Created:', new Date(metadata.creation * 1000))
167
+ console.log('Announce:', metadata.announce, 'Restrict:', metadata.restrict)
168
+
169
+ // Access participants with roles and notify names
170
+ metadata.participants.forEach(p => {
171
+ const role = p.admin === 'superadmin' ? 'Super Admin'
172
+ : p.admin === 'admin' ? 'Admin' : 'Member'
173
+ console.log(`${p.id} - ${role} - Name: ${p.notify || 'N/A'}`)
174
+ })
151
175
 
152
176
  // Create a new group
153
177
  const newGroup = await client.createGroup('Group Name', ['1234567890@s.whatsapp.net'])
@@ -277,7 +301,7 @@ await client.sendButtons('1234567890@s.whatsapp.net', {
277
301
  text: 'Do you like this bot?',
278
302
  title: 'Feedback',
279
303
  subtitle: 'Let us know!',
280
- footer: 'Powered by Baileys',
304
+ footer: 'Powered by Innovators Soft',
281
305
  interactiveButtons: [
282
306
  {
283
307
  name: 'quick_reply',
@@ -295,6 +319,43 @@ await client.sendButtons('1234567890@s.whatsapp.net', {
295
319
  }
296
320
  ]
297
321
  });
322
+
323
+ // Send interactive buttons with an image from URL + caption
324
+ await client.sendButtons('1234567890@s.whatsapp.net', {
325
+ image: { url: 'https://example.com/image.jpg' },
326
+ caption: 'Body',
327
+ title: 'Title',
328
+ subtitle: 'Subtitle',
329
+ footer: 'Footer',
330
+ interactiveButtons: [
331
+ {
332
+ name: 'quick_reply',
333
+ buttonParamsJson: JSON.stringify({
334
+ display_text: 'DisplayText',
335
+ id: 'ID1'
336
+ })
337
+ }
338
+ ],
339
+ hasMediaAttachment: false
340
+ });
341
+
342
+ // Send interactive buttons with a local image file + caption
343
+ await client.sendButtons('1234567890@s.whatsapp.net', {
344
+ imagePath: './image.jpg',
345
+ caption: 'Body',
346
+ title: 'Title',
347
+ subtitle: 'Subtitle',
348
+ footer: 'Footer',
349
+ interactiveButtons: [
350
+ {
351
+ name: 'quick_reply',
352
+ buttonParamsJson: JSON.stringify({
353
+ display_text: 'DisplayText',
354
+ id: 'ID1'
355
+ })
356
+ }
357
+ ]
358
+ });
298
359
  ```
299
360
 
300
361
  #### List Messages
@@ -327,11 +388,72 @@ await client.SendList('1234567890@s.whatsapp.net', {
327
388
  ### 8. Message History
328
389
 
329
390
  ```javascript
330
- // Get chat history
331
- const messages = await client.loadMessages(chatId, 50)
391
+ ### 8. Message Store (History)
392
+
393
+ The library includes a robust message store to keep track of chat history, even across reloads.
394
+
395
+ #### Basic Store Operations
396
+ ```javascript
397
+ // Get all stored messages for a specific chat
398
+ const messages = client.getStoredMessages('1234567890@s.whatsapp.net');
399
+
400
+ // Get all stored messages across all chats
401
+ const allMessages = client.getAllStoredMessages();
332
402
 
333
- // Get specific message
334
- const message = await client.loadMessage(chatId, messageId)
403
+ // Get list of all chat JIDs in the store
404
+ const activeChats = client.getStoredChatIds();
405
+
406
+ // Get statistics about the message store
407
+ const stats = client.getStoreStats();
408
+ ```
409
+
410
+ #### Message Store Persistence
411
+
412
+ The message store can be saved to and loaded from a file for consistency across restarts:
413
+
414
+ ```javascript
415
+ // Initialize client with persistence configuration
416
+ const client = new WhatsAppClient({
417
+ sessionName: 'my-session',
418
+ messageStoreFilePath: './data/my-session/message-store.json', // Optional custom path
419
+ autoSaveInterval: 5 * 60 * 1000, // Auto-save every 5 minutes (default)
420
+ maxMessagesPerChat: 1000, // Maximum messages to keep per chat
421
+ messageTTL: 24 * 60 * 60 * 1000 // Message time-to-live (24 hours default)
422
+ });
423
+
424
+ // The store will automatically:
425
+ // - Load from file when connected
426
+ // - Save to file when disconnected
427
+ // - Auto-save periodically (every 5 minutes by default)
428
+
429
+ // Manual save/load operations
430
+ await client.saveMessageStore(); // Returns {success, path, messageCount, savedAt}
431
+ await client.loadMessageStore(); // Returns {success, messageCount, loadedFrom}
432
+ ```
433
+
434
+ **Features:**
435
+ - 💾 **Auto-save**: Automatically saves the store at regular intervals
436
+ - 🔄 **Auto-load**: Loads the store when connecting
437
+ - 🛡️ **Safe shutdown**: Saves the store before disconnecting
438
+ - 📊 **Change tracking**: Only saves when there are new messages
439
+ - 🗂️ **Custom paths**: Configure where to save the store file
440
+
441
+ #### Events
442
+ The message store emits events you can listen to:
443
+ - `message-stored`: Emitted when messages are added to the cache.
444
+ - `store-loaded`: Emitted when the store is loaded from file.
445
+ - `store-cleared`: Emitted when the entire store is cleared.
446
+ - `chat-store-cleared`: Emitted when a specific chat's history is cleared.
447
+
448
+ ```javascript
449
+ client.on('message-stored', (messages) => {
450
+ console.log('Messages cached:', messages.length);
451
+ });
452
+
453
+ client.on('store-loaded', (info) => {
454
+ console.log(`Loaded ${info.messageCount} messages from file`);
455
+ });
456
+ ```
335
457
  ```
336
458
 
337
459
  ## More Examples and Information
@@ -387,7 +509,7 @@ The library includes example bot commands that you can use:
387
509
  - `!revokeinvite` - Revoke current invite code and generate new one
388
510
  - `!leavegroup` - Leave the current group
389
511
  - `!joingroup <code>` - Join a group by invite code
390
- - `!groupinfo <code>` - Get group info by invite code without joining
512
+ - `!groupinfo [jid|code]` - Get full group details (use in-group, by JID, or by invite code) shows participants, roles, names, and group settings
391
513
  - `!joinrequests` - List pending join requests
392
514
  - `!approvejoin <number>` - Approve a join request
393
515
  - `!rejectjoin <number>` - Reject a join request
@@ -412,6 +534,11 @@ The library includes example bot commands that you can use:
412
534
  - `!list` - Display a scrollable list
413
535
  - `!logout` - Logout from current session
414
536
 
537
+ ### 💾 Message Store
538
+ - `!messages` - Get stored messages for current chat
539
+ - `!message <id>` - Get a specific message by ID
540
+ - `!stats` - View store capacity statistics
541
+
415
542
  ### 🛡️ Protection
416
543
  - `Anti-Delete` - Automatically tracks and emits events for deleted messages
417
544
 
@@ -444,6 +571,24 @@ client.on('disconnected', (error) => {
444
571
  })
445
572
  ```
446
573
 
574
+ ### Contact Events
575
+ ```javascript
576
+ // When contacts are received from history sync
577
+ client.on('contacts-received', (contacts) => {
578
+ console.log(`Received ${contacts.length} contacts`);
579
+ })
580
+
581
+ // When new contacts are added/updated
582
+ client.on('contacts-upsert', (contacts) => {
583
+ console.log(`New Contacts: ${contacts.length} contacts added/updated`);
584
+ })
585
+
586
+ // When existing contacts are updated (profile picture changes, etc.)
587
+ client.on('contacts-update', (updates) => {
588
+ console.log(`Contact Updates: ${updates.length} contacts modified`);
589
+ })
590
+ ```
591
+
447
592
  ### Message Events
448
593
  ```javascript
449
594
  // When a new message is received
@@ -457,7 +602,14 @@ client.on('message', async msg => {
457
602
  // Handle different message types
458
603
  if (msg.hasMedia) {
459
604
  console.log('Message contains media')
460
- // Handle media message
605
+ // Download media (image/video/audio/document)
606
+ const media = await client.downloadMedia(msg)
607
+ if (media) {
608
+ const fs = require('fs')
609
+ const fileName = `./download-${Date.now()}.${media.extension}`
610
+ fs.writeFileSync(fileName, media.buffer)
611
+ console.log('Saved media to:', fileName)
612
+ }
461
613
  }
462
614
  })
463
615
  ```
@@ -832,6 +984,8 @@ For more detailed information about the LID system implementation, see:
832
984
  - [LID_STORE_GUIDE.md](./LID_STORE_GUIDE.md) - Complete implementation guide
833
985
  - [Baileys Migration Guide](https://baileys.wiki/docs/migration/to-v7.0.0/) - Official migration documentation
834
986
  - [example.js](./example.js) - Working examples with LID handling
987
+ - [Read Baileys Documentation](https://innovatorssoftpk.com/)
988
+ - [Deep Knowlege](https://deepwiki.com/innovatorssoft/Baileys)
835
989
 
836
990
  ## Contributing
837
991
 
@@ -844,3 +998,11 @@ This project is licensed under the MIT License - see the LICENSE file for detail
844
998
  ## Credits
845
999
 
846
1000
  Developed by [Innovators Soft](https://facebook.com/innovatorssoft). Based on the [@itsukichan/baileys](https://github.com/itsukichann/baileys) library.
1001
+
1002
+ # Special Thanks
1003
+ - [@whiskeysockets/baileys](https://github.com/whiskeysockets/Baileys)
1004
+ - [@itsukichan](https://github.com/itsukichann)
1005
+ - [All Contributors](https://github.com/innovatorssoft/Baileys/)
1006
+ - [@ZenboBot](https://discordbot.innovatorssoftpk.com/) - AI Powered Baileys Bot
1007
+ # Sponsor Me
1008
+ Buy me a coffee - [Innovators Soft](https://facebook.com/innovatorssoft)
@@ -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...`);
@@ -76,12 +81,36 @@ async function start() {
76
81
  console.log('📦 New LID/PN mappings received')
77
82
  })
78
83
 
84
+ // Handle Contact Events
85
+ client.on('contacts-received', (contacts) => {
86
+ console.log(`\n👥 History Sync: Received ${contacts.length} contacts`);
87
+ })
88
+
89
+ client.on('contacts-upsert', (contacts) => {
90
+ console.log(`\n👥 New Contacts: ${contacts.length} contacts added/updated`);
91
+ })
92
+
93
+ client.on('contacts-update', (updates) => {
94
+ console.log(`\n👥 Contact Updates: ${updates.length} contacts modified`);
95
+ })
96
+
79
97
  // Handle Anti-Delete system
80
98
  client.on('message-deleted', async (data) => {
81
99
  console.log(`\n🛡️ Message from ${data.jid} was deleted!`)
82
100
  await client.sendMessage(data.jid, '⚠️ I saw you deleted that message! I have it saved in my memory. 😉', { quoted: data.originalMessage });
83
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
+ });
84
106
 
107
+ client.on('store-loaded', (info) => {
108
+ console.log(`\n💾 Message store loaded: ${info.messageCount} messages restored from file`);
109
+ });
110
+
111
+ client.on('store-cleared', () => {
112
+ console.log('Main message store has been purged.');
113
+ });
85
114
  // Handle message reactions
86
115
  client.on('message-reaction', async (reaction) => {
87
116
  console.log('\n👍 Message Reaction Received!')
@@ -242,29 +271,6 @@ async function start() {
242
271
  { title: 'Security', id: 'security', description: 'Security and login options' },
243
272
  { title: 'Payments', id: 'payments', description: 'Manage payment methods' },
244
273
  { title: 'Subscriptions', id: 'subscriptions', description: 'View your subscriptions' },
245
- { title: 'Orders', id: 'orders', description: 'View order history' },
246
- { title: 'Wishlist', id: 'wishlist', description: 'Your saved items' },
247
- { title: 'Addresses', id: 'addresses', description: 'Manage shipping addresses' },
248
- { title: 'Help Center', id: 'help', description: 'Get help and support' },
249
- { title: 'Contact Us', id: 'contact', description: 'Reach out to our team' },
250
- { title: 'FAQs', id: 'faqs', description: 'Frequently asked questions' },
251
- { title: 'About Us', id: 'about', description: 'Learn about our company' },
252
- { title: 'Careers', id: 'careers', description: 'Join our team' },
253
- { title: 'Blog', id: 'blog', description: 'Read our latest articles' },
254
- { title: 'Newsletter', id: 'newsletter', description: 'Subscribe to updates' },
255
- { title: 'Events', id: 'events', description: 'Upcoming events' },
256
- { title: 'Webinars', id: 'webinars', description: 'Join live webinars' },
257
- { title: 'Tutorials', id: 'tutorials', description: 'Learn how to use features' },
258
- { title: 'Documentation', id: 'docs', description: 'Technical documentation' },
259
- { title: 'API', id: 'api', description: 'Developer API' },
260
- { title: 'Integrations', id: 'integrations', description: 'Third-party integrations' },
261
- { title: 'Download', id: 'download', description: 'Download our app' },
262
- { title: 'Pricing', id: 'pricing', description: 'View pricing plans' },
263
- { title: 'Upgrade', id: 'upgrade', description: 'Upgrade your plan' },
264
- { title: 'Refer a Friend', id: 'refer', description: 'Earn rewards' },
265
- { title: 'Feedback', id: 'feedback', description: 'Share your thoughts' },
266
- { title: 'Report Issue', id: 'report', description: 'Report a problem' },
267
- { title: 'Language', id: 'language', description: 'Change language' }
268
274
  ]
269
275
  },
270
276
  {
@@ -277,29 +283,6 @@ async function start() {
277
283
  { title: 'Data Saver', id: 'data_saver', description: 'Reduce data usage' },
278
284
  { title: 'Storage', id: 'storage', description: 'Manage local storage' },
279
285
  { title: 'Cache', id: 'cache', description: 'Clear cached data' },
280
- { title: 'Backup', id: 'backup', description: 'Backup your data' },
281
- { title: 'Restore', id: 'restore', description: 'Restore from backup' },
282
- { title: 'Export Data', id: 'export', description: 'Download your data' },
283
- { title: 'Delete Account', id: 'delete', description: 'Permanently remove account' },
284
- { title: 'Terms of Service', id: 'tos', description: 'Read terms and conditions' },
285
- { title: 'Privacy Policy', id: 'privacy_policy', description: 'How we handle your data' },
286
- { title: 'Cookie Policy', id: 'cookies', description: 'About our use of cookies' },
287
- { title: 'Accessibility', id: 'accessibility', description: 'Accessibility features' },
288
- { title: 'Version', id: 'version', description: 'App version information' },
289
- { title: 'Changelog', id: 'changelog', description: 'Recent updates' },
290
- { title: 'Roadmap', id: 'roadmap', description: 'Upcoming features' },
291
- { title: 'Status', id: 'status', description: 'Service status' },
292
- { title: 'Legal', id: 'legal', description: 'Legal information' },
293
- { title: 'Partners', id: 'partners', description: 'Our partners' },
294
- { title: 'Press', id: 'press', description: 'Press resources' },
295
- { title: 'Investors', id: 'investors', description: 'Investor relations' },
296
- { title: 'Affiliates', id: 'affiliates', description: 'Become an affiliate' },
297
- { title: 'Merchandise', id: 'merch', description: 'Official merchandise' },
298
- { title: 'Donate', id: 'donate', description: 'Support our work' },
299
- { title: 'Volunteer', id: 'volunteer', description: 'Get involved' },
300
- { title: 'Community', id: 'community', description: 'Join our community' },
301
- { title: 'Forum', id: 'forum', description: 'Community discussions' },
302
- { title: 'Beta Program', id: 'beta', description: 'Try beta features' }
303
286
  ]
304
287
  }
305
288
  ]
@@ -334,10 +317,10 @@ async function start() {
334
317
 
335
318
  await client.sendButtons(msg.from, {
336
319
  imagePath: './example.jpg',
337
- caption: '', // Keep it short and concise
338
- title: '', // Max 24 chars
339
- subtitle: '', // Optional, appears below title
340
- footer: '',
320
+ caption: 'here is captions of image\nwith linebreaks', // Keep it short and concise
321
+ title: 'Image Title', // Max 24 chars
322
+ subtitle: 'Image Subtitle (but optional)', // Optional, appears below title
323
+ footer: 'Image Footer',
341
324
  interactiveButtons: [
342
325
  {
343
326
  name: 'quick_reply',
@@ -415,7 +398,7 @@ async function start() {
415
398
  `• !revokeinvite - Revoke group invite code\n` +
416
399
  `• !leavegroup - Leave the group\n` +
417
400
  `• !joingroup <code> - Join group by invite code\n` +
418
- `• !groupinfo <code> - Get group info by invite code\n` +
401
+ `• !groupinfo [jid|code] - Full group details with participants\n` +
419
402
  `• !joinrequests - List pending join requests\n` +
420
403
  `• !approvejoin <number> - Approve join request\n` +
421
404
  `• !rejectjoin <number> - Reject join request\n` +
@@ -439,7 +422,13 @@ async function start() {
439
422
  `• !buttons - Button template\n` +
440
423
  `• !list - Scrollable list\n\n` +
441
424
 
442
- `*🔐 LID/PN/JID Management*\n` +
425
+ `*� Message Store*\n` +
426
+ `• !messages - Get stored messages for this chat\n` +
427
+ `• !allmessages - Get statistics for all stored chats\n` +
428
+ `• !message - Get a specific message by ID\n` +
429
+ `• !stats - Get store capacity statistics\n\n` +
430
+
431
+ `*�🔐 LID/PN/JID Management*\n` +
443
432
  `• !lid - Get your LID\n` +
444
433
  `• !pn <lid> - Get PN from LID\n` +
445
434
  `• !parse <jid> - Parse JID info\n` +
@@ -467,6 +456,7 @@ async function start() {
467
456
  groups.forEach((group, index) => {
468
457
  groupList += `${index + 1}. *${group.subject}*\n`
469
458
  groupList += ` ID: ${group.id}\n`
459
+ if (group.notify) groupList += ` Notify: ${group.notify}\n`
470
460
  groupList += ` Members: ${group.participants.length}\n`
471
461
  if (group.desc) groupList += ` Description: ${group.desc}\n`
472
462
  groupList += '\n'
@@ -481,6 +471,45 @@ async function start() {
481
471
  }
482
472
  break
483
473
 
474
+ case '!groupinfo':
475
+ try {
476
+ // Use provided group JID or current group
477
+ const groupJid = args.trim() || msg.raw.key.remoteJid
478
+ if (!groupJid || !groupJid.endsWith('@g.us')) {
479
+ await client.sendMessage(msg.from, '❌ Please provide a group JID or use this command in a group.\nUsage: !groupinfo <groupJid>')
480
+ break
481
+ }
482
+ const groupInfo = await client.getGroupMetadata(groupJid)
483
+ if (groupInfo) {
484
+ let groupList = `*Group Info:*\n\n` +
485
+ `ID: ${groupInfo.id}\n` +
486
+ `Notify: ${groupInfo.notify || 'N/A'}\n` +
487
+ `Subject: ${groupInfo.subject}\n` +
488
+ `Owner: ${groupInfo.owner || 'N/A'}\n` +
489
+ `Created: ${new Date(groupInfo.creation * 1000).toLocaleString()}\n` +
490
+ `Members: ${groupInfo.participants.length}\n` +
491
+ `Description: ${groupInfo.desc || 'N/A'}\n\n` +
492
+ `*👥 Participants:*\n\n`
493
+
494
+ groupInfo.participants.forEach((p, i) => {
495
+ const role = p.admin === 'superadmin' ? '👑 Super Admin'
496
+ : p.admin === 'admin' ? '🛡️ Admin'
497
+ : '👤 Member'
498
+ groupList += `${i + 1}. ${p.id}\n`
499
+ groupList += ` Role: ${role}\n`
500
+ if (p.notify) groupList += ` Name: ${p.notify}\n`
501
+ groupList += '\n'
502
+ })
503
+
504
+ await client.sendMessage(msg.from, groupList)
505
+ } else {
506
+ await client.sendMessage(msg.from, 'Group not found')
507
+ }
508
+ } catch (error) {
509
+ console.error('Error fetching group info:', error)
510
+ await client.sendMessage(msg.from, 'Failed to fetch group info')
511
+ }
512
+ break
484
513
  case '!logout':
485
514
  // Ask for confirmation before logging out
486
515
  await client.sendButtons(msg.from, {
@@ -864,22 +893,65 @@ async function start() {
864
893
 
865
894
  case '!groupinfo':
866
895
  try {
867
- if (!args) {
868
- await client.sendMessage(msg.from, '❌ Please provide an invite code.\nUsage: !groupinfo AbCdEfGhIjK');
869
- break;
896
+ const input = args.trim()
897
+ let groupInfoResult
898
+
899
+ if (!input) {
900
+ // No args: use current group
901
+ if (!msg.raw.key.remoteJid.endsWith('@g.us')) {
902
+ 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')
903
+ break
904
+ }
905
+ groupInfoResult = await client.getGroupMetadata(msg.raw.key.remoteJid)
906
+ } else if (input.includes('@g.us')) {
907
+ // Argument is a group JID
908
+ groupInfoResult = await client.getGroupMetadata(input)
909
+ } else {
910
+ // Argument is an invite code (or full link)
911
+ groupInfoResult = await client.getGroupInfoByInviteCode(input)
870
912
  }
871
- const groupInfoResult = await client.getGroupInfoByInviteCode(args.trim());
872
- let infoText = `*📋 Group Info*\n\n`;
873
- infoText += `Name: *${groupInfoResult.subject || 'N/A'}*\n`;
874
- infoText += `ID: ${groupInfoResult.id || 'N/A'}\n`;
875
- infoText += `Creator: ${groupInfoResult.owner || 'N/A'}\n`;
876
- infoText += `Description: ${groupInfoResult.desc || 'No description'}\n`;
877
- infoText += `Members: ${groupInfoResult.size || groupInfoResult.participants?.length || 'N/A'}\n`;
878
- infoText += `Created: ${groupInfoResult.creation ? new Date(groupInfoResult.creation * 1000).toLocaleString() : 'N/A'}`;
879
- await client.sendMessage(msg.from, infoText);
913
+
914
+ if (!groupInfoResult) {
915
+ await client.sendMessage(msg.from, '❌ Group not found.')
916
+ break
917
+ }
918
+
919
+ // Build group info header
920
+ let infoText = `*📋 Group Info*\n\n`
921
+ infoText += `*Name:* ${groupInfoResult.subject || 'N/A'}\n`
922
+ infoText += `*ID:* ${groupInfoResult.id || 'N/A'}\n`
923
+ if (groupInfoResult.notify) infoText += `*Notify:* ${groupInfoResult.notify}\n`
924
+ infoText += `*Owner:* ${groupInfoResult.owner || 'N/A'}\n`
925
+ infoText += `*Created:* ${groupInfoResult.creation ? new Date(groupInfoResult.creation * 1000).toLocaleString() : 'N/A'}\n`
926
+ infoText += `*Size:* ${groupInfoResult.size || groupInfoResult.participants?.length || 'N/A'}\n`
927
+ infoText += `*Description:* ${groupInfoResult.desc || 'No description'}\n`
928
+ if (groupInfoResult.announce !== undefined) infoText += `*Announce:* ${groupInfoResult.announce ? 'Yes (admins only)' : 'No'}\n`
929
+ if (groupInfoResult.restrict !== undefined) infoText += `*Restricted:* ${groupInfoResult.restrict ? 'Yes (admins only edit info)' : 'No'}\n`
930
+ if (groupInfoResult.ephemeralDuration) infoText += `*Disappearing:* ${groupInfoResult.ephemeralDuration}s\n`
931
+ if (groupInfoResult.memberAddMode !== undefined) infoText += `*Member Add:* ${groupInfoResult.memberAddMode ? 'All members' : 'Admins only'}\n`
932
+ if (groupInfoResult.isCommunity) infoText += `*Community:* Yes\n`
933
+ if (groupInfoResult.linkedParent) infoText += `*Linked Parent:* ${groupInfoResult.linkedParent}\n`
934
+
935
+ // Build participants list if available
936
+ if (groupInfoResult.participants && groupInfoResult.participants.length > 0) {
937
+ infoText += `\n*👥 Participants (${groupInfoResult.participants.length}):*\n\n`
938
+ groupInfoResult.participants.forEach((p, i) => {
939
+ const role = p.admin === 'superadmin' ? '👑 Super Admin'
940
+ : p.admin === 'admin' ? '🛡️ Admin'
941
+ : '👤 Member'
942
+ infoText += `${i + 1}. ${p.id}\n`
943
+ infoText += ` Role: ${role}\n`
944
+ if (p.notify) infoText += ` Name: ${p.notify}\n`
945
+ if (p.lid) infoText += ` LID: ${p.lid}\n`
946
+ if (p.phoneNumber) infoText += ` Phone: ${p.phoneNumber}\n`
947
+ infoText += '\n'
948
+ })
949
+ }
950
+
951
+ await client.sendMessage(msg.from, infoText)
880
952
  } catch (error) {
881
- console.error('Error getting group info:', error);
882
- await client.sendMessage(msg.from, `❌ Failed to get group info: ${error.message}`);
953
+ console.error('Error getting group info:', error)
954
+ await client.sendMessage(msg.from, `❌ Failed to get group info: ${error.message}`)
883
955
  }
884
956
  break;
885
957
 
@@ -1188,6 +1260,72 @@ async function start() {
1188
1260
  await client.sendMessage(msg.from, `❌ Failed to update: ${error.message}`);
1189
1261
  }
1190
1262
  break;
1263
+
1264
+ case '!messages':
1265
+ const history = client.getStoredMessages(msg.from);
1266
+ let historyText = `*💾 Stored Messages for this chat (${history.length}):*\n\n`;
1267
+ history.slice(-10).forEach((m, i) => {
1268
+ const content = m.message.conversation || m.message.extendedTextMessage?.text || "[Media/Other]";
1269
+ historyText += `${i + 1}. ID: ${m.key.id}\n Text: ${content.substring(0, 50)}${content.length > 50 ? '...' : ''}\n\n`;
1270
+ });
1271
+ await client.sendMessage(msg.from, historyText);
1272
+ break;
1273
+
1274
+ case '!message':
1275
+ if (!args) {
1276
+ await client.sendMessage(msg.from, "❌ Please provide a message ID.");
1277
+ break;
1278
+ }
1279
+ const storedMsg = client.getStoredMessage({ remoteJid: msg.from, id: args.trim(), fromMe: false });
1280
+ if (storedMsg) {
1281
+ await client.sendMessage(msg.from, `✅ Found message!\n\nContent: ${JSON.stringify(storedMsg.message, null, 2).substring(0, 1000)}`);
1282
+ } else {
1283
+ await client.sendMessage(msg.from, "❌ Message not found in store.");
1284
+ }
1285
+ break;
1286
+
1287
+ case '!stats':
1288
+ const stats = client.getStoreStats();
1289
+ await client.sendMessage(msg.from, `*📊 Message Store Statistics*\n\n• Total Chats: ${stats.totalChats}\n• Total Messages: ${stats.totalMessages}\n• Total Deleted: ${stats.totalDeleted}`);
1290
+ break;
1291
+
1292
+ case '!allmessages':
1293
+ const allMsgs = client.getAllStoredMessages();
1294
+ const activeChats = client.getStoredChatIds();
1295
+ 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')}`);
1296
+ break;
1297
+
1298
+ case '!savestore':
1299
+ await client.sendMessage(msg.from, '💾 Saving message store to file...');
1300
+ const saveResult = await client.saveMessageStore();
1301
+ if (saveResult.success) {
1302
+ await client.sendMessage(msg.from,
1303
+ `✅ *Store Saved Successfully*\n\n` +
1304
+ `• Messages: ${saveResult.messageCount}\n` +
1305
+ `• Path: ${saveResult.path}\n` +
1306
+ `• Saved at: ${saveResult.savedAt.toLocaleString()}`
1307
+ );
1308
+ } else {
1309
+ await client.sendMessage(msg.from, `❌ Failed to save store: ${saveResult.error}`);
1310
+ }
1311
+ break;
1312
+
1313
+ case '!loadstore':
1314
+ await client.sendMessage(msg.from, '📂 Loading message store from file...');
1315
+ const loadResult = await client.loadMessageStore();
1316
+ if (loadResult.success) {
1317
+ await client.sendMessage(msg.from,
1318
+ `✅ *Store Loaded Successfully*\n\n` +
1319
+ `• Messages: ${loadResult.messageCount}\n` +
1320
+ `• Loaded from: ${loadResult.loadedFrom}`
1321
+ );
1322
+ } else {
1323
+ await client.sendMessage(msg.from,
1324
+ `⚠️ Could not load store\n` +
1325
+ `Reason: ${loadResult.reason || loadResult.error}`
1326
+ );
1327
+ }
1328
+ break;
1191
1329
  }
1192
1330
  })
1193
1331
 
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
@@ -61,6 +62,16 @@ class WhatsAppClient extends EventEmitter {
61
62
  maxMessagesPerChat: config.maxMessagesPerChat || 1000,
62
63
  ttl: config.messageTTL || 24 * 60 * 60 * 1000
63
64
  });
65
+ // Initialize contacts cache
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;
64
75
  }
65
76
 
66
77
  /**
@@ -115,7 +126,7 @@ class WhatsAppClient extends EventEmitter {
115
126
  }
116
127
 
117
128
  const browserConfig = this.authmethod === 'pairing'
118
- ? Browsers.windows('chrome')
129
+ ? Browsers.iOS('chrome')
119
130
  : ["Innovators Soft", "chrome", "1000.26100.275.0"];
120
131
 
121
132
  const { version: baileysVersion, isLatest: baileysIsLatest } = await fetchLatestBaileysVersion();
@@ -132,6 +143,7 @@ class WhatsAppClient extends EventEmitter {
132
143
  logger,
133
144
  markOnlineOnConnect: false,
134
145
  syncFullHistory: true,
146
+ getMessage: async (key) => (this.messageStore.getOriginalMessage(key))?.message,
135
147
  generateHighQualityLinkPreview: true,
136
148
  linkPreviewImageThumbnailWidth: 192,
137
149
  emitOwnEvents: true,
@@ -150,15 +162,16 @@ class WhatsAppClient extends EventEmitter {
150
162
  console.error(`Error fetching metadata for group ${jid}:`, error);
151
163
  return null;
152
164
  }
153
- }
165
+ },
154
166
  });
155
167
 
156
168
  this.store = this.sock.signalRepository.lidMapping;
157
169
 
158
-
159
-
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);
@@ -167,15 +180,21 @@ class WhatsAppClient extends EventEmitter {
167
180
  if (this._connectionState !== 'connected') {
168
181
  const user = getCurrentSenderInfo(this.sock.authState)
169
182
  if (user) {
183
+ this.isConnected = true;
170
184
  const userInfo = {
171
185
  name: user.pushName || 'Unknown',
172
186
  phone: user.phoneNumber,
173
187
  platform: user.platform || 'Unknown',
174
- isOnline: true
188
+ isOnline: true,
175
189
  };
176
- this.isConnected = true;
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;
@@ -387,6 +436,8 @@ class WhatsAppClient extends EventEmitter {
387
436
 
388
437
  // 👍 Handle message reactions
389
438
  this.sock.ev.on('messages.reaction', async (reactions) => {
439
+
440
+
390
441
  try {
391
442
  for (const reaction of reactions) {
392
443
  // Get the chat JID, preferring PN over LID
@@ -509,6 +560,34 @@ class WhatsAppClient extends EventEmitter {
509
560
  }
510
561
  });
511
562
 
563
+ // Handle contacts from history sync
564
+ this.sock.ev.on('messaging-history.set', ({ contacts: newContacts }) => {
565
+ if (newContacts && newContacts.length > 0) {
566
+ for (const contact of newContacts) {
567
+ this.contactsCache.set(contact.id, contact);
568
+ }
569
+ this.emit('contacts-received', newContacts);
570
+
571
+ }
572
+ });
573
+
574
+ // Handle contacts upsert (new contacts added)
575
+ this.sock.ev.on('contacts.upsert', (newContacts) => {
576
+ for (const contact of newContacts) {
577
+ this.contactsCache.set(contact.id, contact);
578
+ }
579
+ this.emit('contacts-upsert', newContacts);
580
+ });
581
+
582
+ // Handle contacts update (profile picture changes, etc.)
583
+ this.sock.ev.on('contacts.update', (updates) => {
584
+ for (const update of updates) {
585
+ const existing = this.contactsCache.get(update.id) || {};
586
+ this.contactsCache.set(update.id, { ...existing, ...update });
587
+ }
588
+ this.emit('contacts-update', updates);
589
+ });
590
+
512
591
  } catch (error) {
513
592
  console.error('Error in connect:', error);
514
593
  this.emit('error', error);
@@ -654,6 +733,10 @@ class WhatsAppClient extends EventEmitter {
654
733
  throw new Error('Unsupported file type: ' + fileExtension);
655
734
  }
656
735
 
736
+ if (options.mentions) {
737
+ mediaMessage.mentions = options.mentions;
738
+ }
739
+
657
740
  return await this.sock.sendMessage(chatId, mediaMessage, { ai: true });
658
741
  } catch (error) {
659
742
  console.error('Error sending media:', error);
@@ -684,11 +767,20 @@ class WhatsAppClient extends EventEmitter {
684
767
  const fileName = path.basename(filePath);
685
768
  const mimeType = mime.getType(filePath);
686
769
 
687
- return await this.sock.sendMessage(chatId, {
770
+ const messageContent = {
688
771
  document: fileBuffer,
689
772
  caption: caption,
690
773
  mimetype: mimeType,
691
774
  fileName: fileName,
775
+ };
776
+
777
+ if (typeof caption === 'object' && caption !== null) {
778
+ if (caption.caption) messageContent.caption = caption.caption;
779
+ if (caption.mentions) messageContent.mentions = caption.mentions;
780
+ }
781
+
782
+ return await this.sock.sendMessage(chatId, {
783
+ ...messageContent,
692
784
  }, { ai: true });
693
785
  } catch (error) {
694
786
  console.error('Error sending document:', error);
@@ -719,8 +811,16 @@ class WhatsAppClient extends EventEmitter {
719
811
  const {
720
812
  text,
721
813
  imagePath,
814
+ image,
815
+ video,
816
+ document,
817
+ location,
818
+ product,
819
+ mimetype,
820
+ jpegThumbnail,
722
821
  caption,
723
822
  title,
823
+ subtitle,
724
824
  footer,
725
825
  interactiveButtons = [],
726
826
  hasMediaAttachment = false,
@@ -729,24 +829,40 @@ class WhatsAppClient extends EventEmitter {
729
829
  let messageContent = {};
730
830
 
731
831
  try {
832
+ const base = {
833
+ title: title,
834
+ subtitle: subtitle,
835
+ footer: footer,
836
+ interactiveButtons: interactiveButtons,
837
+ hasMediaAttachment: hasMediaAttachment,
838
+ };
839
+
732
840
  if (imagePath) {
733
- // Handle message with image
841
+ // Handle message with local image path
734
842
  const imageBuffer = fs.readFileSync(imagePath);
735
843
  messageContent = {
844
+ ...base,
736
845
  image: imageBuffer,
737
846
  caption: caption,
738
- title: title,
739
- footer: footer,
740
- interactiveButtons: interactiveButtons,
741
- hasMediaAttachment: hasMediaAttachment,
847
+ };
848
+ } else if (image || video || document || location || product) {
849
+ // Pass-through media objects (e.g. { image: { url } })
850
+ messageContent = {
851
+ ...base,
852
+ ...(image ? { image } : {}),
853
+ ...(video ? { video } : {}),
854
+ ...(document ? { document } : {}),
855
+ ...(location ? { location } : {}),
856
+ ...(product ? { product } : {}),
857
+ ...(mimetype ? { mimetype } : {}),
858
+ ...(jpegThumbnail ? { jpegThumbnail } : {}),
859
+ caption: caption,
742
860
  };
743
861
  } else {
744
862
  // Handle text-only message
745
863
  messageContent = {
864
+ ...base,
746
865
  text: text,
747
- title: title,
748
- footer: footer,
749
- interactiveButtons: interactiveButtons,
750
866
  };
751
867
  }
752
868
 
@@ -1183,6 +1299,12 @@ class WhatsAppClient extends EventEmitter {
1183
1299
  this.sock = null;
1184
1300
  }
1185
1301
 
1302
+ // Clear store interval
1303
+ if (this._storeInterval) {
1304
+ clearInterval(this._storeInterval);
1305
+ this._storeInterval = null;
1306
+ }
1307
+
1186
1308
  // Add a small delay before reconnecting
1187
1309
  await new Promise(resolve => setTimeout(resolve, 1000));
1188
1310
 
@@ -1332,6 +1454,7 @@ class WhatsAppClient extends EventEmitter {
1332
1454
 
1333
1455
  // Update connection state and emit event
1334
1456
  this.isConnected = false;
1457
+
1335
1458
  this.emit('logout', 'Logged out successfully');
1336
1459
 
1337
1460
  return true;
@@ -1343,11 +1466,11 @@ class WhatsAppClient extends EventEmitter {
1343
1466
  }
1344
1467
 
1345
1468
  /**
1346
- * Download media from a message
1347
- * @param {object} message - The message object containing media (must have raw property)
1348
- * @returns {Promise<object|null>} Object with buffer, mimetype, and extension, or null if no media
1349
- * @throws {Error} If client is not connected or download fails
1350
- */
1469
+ * Download media from a message
1470
+ * @param {object} message - The message object containing media (must have raw property)
1471
+ * @returns {Promise<object|null>} Object with buffer, mimetype, and extension, or null if no media
1472
+ * @throws {Error} If client is not connected or download fails
1473
+ */
1351
1474
  async downloadMedia(message) {
1352
1475
  if (!this.isConnected) {
1353
1476
  throw new Error('Client is not connected');
@@ -1461,6 +1584,254 @@ class WhatsAppClient extends EventEmitter {
1461
1584
  }
1462
1585
  }
1463
1586
 
1587
+ /**
1588
+ * Get all stored messages for a specific chat
1589
+ * @param {string} chatId - The JID of the chat
1590
+ * @returns {Array} Array of stored messages (WebMessageInfo)
1591
+ */
1592
+ getStoredMessages(chatId) {
1593
+ return this.messageStore.getChatMessages(chatId);
1594
+ }
1595
+
1596
+ /**
1597
+ * Get a specific stored message by its key
1598
+ * @param {object} key - The message key { remoteJid, id, fromMe }
1599
+ * @returns {object|undefined} The stored message (WebMessageInfo)
1600
+ */
1601
+ getStoredMessage(key) {
1602
+ return this.messageStore.getOriginalMessage(key);
1603
+ }
1604
+
1605
+ /**
1606
+ * Get statistics about the message store
1607
+ * @returns {object} Store statistics
1608
+ */
1609
+ getStoreStats() {
1610
+ return this.messageStore.getStats();
1611
+ }
1612
+
1613
+ /**
1614
+ * Get all stored messages from all chats
1615
+ * @returns {Array} Array of all stored messages
1616
+ */
1617
+ getAllStoredMessages() {
1618
+ return this.messageStore.getChatIds().flatMap(chatId => this.messageStore.getChatMessages(chatId));
1619
+ }
1620
+
1621
+ /**
1622
+ * Get IDs of all chats that have stored messages
1623
+ * @returns {Array<string>} Array of chat JIDs
1624
+ */
1625
+ getStoredChatIds() {
1626
+ return this.messageStore.getChatIds();
1627
+ }
1628
+
1629
+ /**
1630
+ * Clear the entire message store
1631
+ */
1632
+ clearMessageStore() {
1633
+ this.messageStore.clear();
1634
+ this.emit('store-cleared');
1635
+ }
1636
+
1637
+ /**
1638
+ * Clear stored messages for a specific chat
1639
+ * @param {string} chatId - The JID of the chat to clear
1640
+ */
1641
+ clearChatStore(chatId) {
1642
+ this.messageStore.clearChat(chatId);
1643
+ this.emit('chat-store-cleared', chatId);
1644
+ }
1645
+
1646
+ /**
1647
+ * Save the message store to a JSON file
1648
+ * @returns {Promise<{success: boolean, path: string, messageCount: number}>}
1649
+ */
1650
+ async saveMessageStore() {
1651
+ try {
1652
+ const rawAllMessages = this.messageStore.getAllMessages();
1653
+ const stats = this.messageStore.getStats();
1654
+
1655
+ let allMessages = [];
1656
+ if (Array.isArray(rawAllMessages)) {
1657
+ allMessages = rawAllMessages;
1658
+ } else if (rawAllMessages && typeof rawAllMessages[Symbol.iterator] === 'function') {
1659
+ allMessages = Array.from(rawAllMessages);
1660
+ } else if (rawAllMessages && typeof rawAllMessages === 'object') {
1661
+ allMessages = Object.values(rawAllMessages);
1662
+ } else {
1663
+ try {
1664
+ allMessages = this.messageStore.getChatIds().flatMap(chatId => this.messageStore.getChatMessages(chatId));
1665
+ } catch {
1666
+ allMessages = [];
1667
+ }
1668
+ }
1669
+
1670
+ // Serialize the store data
1671
+ const storeData = {
1672
+ version: '1.0.0',
1673
+ savedAt: new Date().toISOString(),
1674
+ sessionName: this.sessionName,
1675
+ stats: stats,
1676
+ messages: []
1677
+ };
1678
+
1679
+ // Convert Map structure to serializable array format
1680
+ for (const msg of allMessages) {
1681
+ try {
1682
+ storeData.messages.push({
1683
+ key: msg.key,
1684
+ message: msg.message,
1685
+ messageTimestamp: msg.messageTimestamp,
1686
+ pushName: msg.pushName,
1687
+ broadcast: msg.broadcast,
1688
+ isDeleted: msg.isDeleted
1689
+ });
1690
+ } catch (err) {
1691
+ console.error('Error serializing message:', err);
1692
+ }
1693
+ }
1694
+
1695
+ // Ensure directory exists
1696
+ const dir = path.dirname(this.messageStoreFilePath);
1697
+ if (!fs.existsSync(dir)) {
1698
+ fs.mkdirSync(dir, { recursive: true });
1699
+ }
1700
+
1701
+ // Write to file
1702
+ fs.writeFileSync(
1703
+ this.messageStoreFilePath,
1704
+ JSON.stringify(storeData, null, 2),
1705
+ 'utf8'
1706
+ );
1707
+
1708
+ this._lastStoreSave = new Date();
1709
+ this._storeChangeCount = 0;
1710
+
1711
+ // console.log(`[MessageStore] Saved ${storeData.messages.length} messages to ${this.messageStoreFilePath}`);
1712
+
1713
+ return {
1714
+ success: true,
1715
+ path: this.messageStoreFilePath,
1716
+ messageCount: storeData.messages.length,
1717
+ savedAt: this._lastStoreSave
1718
+ };
1719
+ } catch (error) {
1720
+ console.error('[MessageStore] Error saving store:', error);
1721
+ return {
1722
+ success: false,
1723
+ error: error.message
1724
+ };
1725
+ }
1726
+ }
1727
+
1728
+ /**
1729
+ * Load the message store from a JSON file
1730
+ * @returns {Promise<{success: boolean, messageCount: number}>}
1731
+ */
1732
+ async loadMessageStore() {
1733
+ try {
1734
+ if (!fs.existsSync(this.messageStoreFilePath)) {
1735
+ // console.log('[MessageStore] No saved store file found');
1736
+ return { success: false, reason: 'file_not_found' };
1737
+ }
1738
+
1739
+ const fileContent = fs.readFileSync(this.messageStoreFilePath, 'utf8');
1740
+ const storeData = JSON.parse(fileContent);
1741
+
1742
+ // Validate data structure
1743
+ if (!storeData.messages || !Array.isArray(storeData.messages)) {
1744
+ throw new Error('Invalid store file format');
1745
+ }
1746
+
1747
+ // Clear existing store
1748
+ this.messageStore.clear();
1749
+
1750
+ // Load messages into the store
1751
+ let loadedCount = 0;
1752
+ for (const msgData of storeData.messages) {
1753
+ try {
1754
+ // Use the message store's internal handler to properly store messages
1755
+ const storeHandler = createMessageStoreHandler(this.messageStore);
1756
+ storeHandler({
1757
+ messages: [{
1758
+ key: msgData.key,
1759
+ message: msgData.message,
1760
+ messageTimestamp: msgData.messageTimestamp,
1761
+ pushName: msgData.pushName,
1762
+ broadcast: msgData.broadcast
1763
+ }],
1764
+ type: 'append'
1765
+ });
1766
+ loadedCount++;
1767
+ } catch (err) {
1768
+ console.error('[MessageStore] Error loading message:', err);
1769
+ }
1770
+ }
1771
+
1772
+ this._lastStoreSave = new Date(storeData.savedAt);
1773
+ this._storeChangeCount = 0;
1774
+
1775
+ // console.log(`[MessageStore] Loaded ${loadedCount}/${storeData.messages.length} messages from ${this.messageStoreFilePath}`);
1776
+
1777
+ this.emit('store-loaded', { messageCount: loadedCount });
1778
+
1779
+ return {
1780
+ success: true,
1781
+ messageCount: loadedCount,
1782
+ loadedFrom: storeData.savedAt
1783
+ };
1784
+ } catch (error) {
1785
+ console.error('[MessageStore] Error loading store:', error);
1786
+ return {
1787
+ success: false,
1788
+ error: error.message
1789
+ };
1790
+ }
1791
+ }
1792
+
1793
+ /**
1794
+ * Start auto-save timer
1795
+ * @private
1796
+ */
1797
+ _startAutoSave() {
1798
+ if (this._autoSaveTimer) {
1799
+ clearInterval(this._autoSaveTimer);
1800
+ }
1801
+
1802
+ if (this.autoSaveInterval > 0) {
1803
+ this._autoSaveTimer = setInterval(async () => {
1804
+ // Only save if there have been changes
1805
+ if (this._storeChangeCount > 0) {
1806
+ // console.log(`[MessageStore] Auto-saving (${this._storeChangeCount} changes since last save)`);
1807
+ await this.saveMessageStore();
1808
+ }
1809
+ }, this.autoSaveInterval);
1810
+
1811
+ // console.log(`[MessageStore] Auto-save enabled (interval: ${this.autoSaveInterval}ms)`);
1812
+ }
1813
+ }
1814
+
1815
+ /**
1816
+ * Stop auto-save timer
1817
+ * @private
1818
+ */
1819
+ _stopAutoSave() {
1820
+ if (this._autoSaveTimer) {
1821
+ clearInterval(this._autoSaveTimer);
1822
+ this._autoSaveTimer = null;
1823
+ // console.log('[MessageStore] Auto-save disabled');
1824
+ }
1825
+ }
1826
+
1827
+ /**
1828
+ * Increment the store change counter (called when messages are added)
1829
+ * @private
1830
+ */
1831
+ _incrementStoreChangeCount() {
1832
+ this._storeChangeCount++;
1833
+ }
1834
+
1464
1835
  // ═══════════════════════════════════════════════════════════
1465
1836
  // 📁 GROUP MANAGEMENT METHODS
1466
1837
  // ═══════════════════════════════════════════════════════════
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "innovators-bot2",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "WhatsApp API",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "start": "node example.js",
8
+ "dev": "node --watch example.js",
8
9
  "publish:all": "node publish-dual.js"
9
10
  },
10
11
  "keywords": [
@@ -29,7 +30,7 @@
29
30
  },
30
31
  "homepage": "https://github.com/innovatorssoft/WhatsAppAPI?tab=readme-ov-file#whatsapp-api",
31
32
  "dependencies": {
32
- "@innovatorssoft/baileys": "^7.4.0",
33
+ "@innovatorssoft/baileys": "latest",
33
34
  "figlet": "^1.8.0",
34
35
  "mime": "^3.0.0",
35
36
  "mime-types": "^2.1.35",
@@ -38,4 +39,4 @@
38
39
  "qrcode-terminal": "^0.12.0",
39
40
  "wa-sticker-formatter": "^4.4.4"
40
41
  }
41
- }
42
+ }