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 +103 -7
- package/STORE_PERSISTENCE.md +62 -0
- package/example.js +278 -17
- package/index.js +328 -5
- package/package.json +3 -3
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
|
-
|
|
382
|
-
const messages = await client.loadMessages(chatId, 50)
|
|
409
|
+
### 8. Message Store (History)
|
|
383
410
|
|
|
384
|
-
|
|
385
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
835
|
-
|
|
836
|
-
|
|
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
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
infoText
|
|
846
|
-
|
|
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.
|
|
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 (
|
|
161
|
-
|
|
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
|
-
|
|
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)
|
|
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.
|
|
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": "
|
|
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
|
+
}
|