@kangwifi-pro/waliwa 1.0.0
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/LICENSE +21 -0
- package/README.md +600 -0
- package/dist/advanced/features.d.ts +211 -0
- package/dist/advanced/features.d.ts.map +1 -0
- package/dist/advanced/features.js +671 -0
- package/dist/advanced/features.js.map +1 -0
- package/dist/auth/auth-state.d.ts +89 -0
- package/dist/auth/auth-state.d.ts.map +1 -0
- package/dist/auth/auth-state.js +330 -0
- package/dist/auth/auth-state.js.map +1 -0
- package/dist/auth/pairing-code.d.ts +78 -0
- package/dist/auth/pairing-code.d.ts.map +1 -0
- package/dist/auth/pairing-code.js +191 -0
- package/dist/auth/pairing-code.js.map +1 -0
- package/dist/auth/qr-code.d.ts +60 -0
- package/dist/auth/qr-code.d.ts.map +1 -0
- package/dist/auth/qr-code.js +151 -0
- package/dist/auth/qr-code.js.map +1 -0
- package/dist/calls/handler.d.ts +61 -0
- package/dist/calls/handler.d.ts.map +1 -0
- package/dist/calls/handler.js +117 -0
- package/dist/calls/handler.js.map +1 -0
- package/dist/core/binary.d.ts +74 -0
- package/dist/core/binary.d.ts.map +1 -0
- package/dist/core/binary.js +448 -0
- package/dist/core/binary.js.map +1 -0
- package/dist/core/crypto.d.ts +60 -0
- package/dist/core/crypto.d.ts.map +1 -0
- package/dist/core/crypto.js +170 -0
- package/dist/core/crypto.js.map +1 -0
- package/dist/core/noise.d.ts +106 -0
- package/dist/core/noise.d.ts.map +1 -0
- package/dist/core/noise.js +307 -0
- package/dist/core/noise.js.map +1 -0
- package/dist/events/emitter.d.ts +44 -0
- package/dist/events/emitter.d.ts.map +1 -0
- package/dist/events/emitter.js +79 -0
- package/dist/events/emitter.js.map +1 -0
- package/dist/features/index.d.ts +342 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +755 -0
- package/dist/features/index.js.map +1 -0
- package/dist/fixes/index.d.ts +333 -0
- package/dist/fixes/index.d.ts.map +1 -0
- package/dist/fixes/index.js +762 -0
- package/dist/fixes/index.js.map +1 -0
- package/dist/groups/management.d.ts +86 -0
- package/dist/groups/management.d.ts.map +1 -0
- package/dist/groups/management.js +443 -0
- package/dist/groups/management.js.map +1 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +236 -0
- package/dist/index.js.map +1 -0
- package/dist/messages/media.d.ts +93 -0
- package/dist/messages/media.d.ts.map +1 -0
- package/dist/messages/media.js +252 -0
- package/dist/messages/media.js.map +1 -0
- package/dist/messages/message-encoder.d.ts +70 -0
- package/dist/messages/message-encoder.d.ts.map +1 -0
- package/dist/messages/message-encoder.js +453 -0
- package/dist/messages/message-encoder.js.map +1 -0
- package/dist/messages/send.d.ts +101 -0
- package/dist/messages/send.d.ts.map +1 -0
- package/dist/messages/send.js +409 -0
- package/dist/messages/send.js.map +1 -0
- package/dist/recovery/index.d.ts +125 -0
- package/dist/recovery/index.d.ts.map +1 -0
- package/dist/recovery/index.js +584 -0
- package/dist/recovery/index.js.map +1 -0
- package/dist/skdm/index.d.ts +220 -0
- package/dist/skdm/index.d.ts.map +1 -0
- package/dist/skdm/index.js +600 -0
- package/dist/skdm/index.js.map +1 -0
- package/dist/socket/hybrid.d.ts +118 -0
- package/dist/socket/hybrid.d.ts.map +1 -0
- package/dist/socket/hybrid.js +352 -0
- package/dist/socket/hybrid.js.map +1 -0
- package/dist/socket/wa-socket.d.ts +300 -0
- package/dist/socket/wa-socket.d.ts.map +1 -0
- package/dist/socket/wa-socket.js +1094 -0
- package/dist/socket/wa-socket.js.map +1 -0
- package/dist/socket/ws-socket.d.ts +96 -0
- package/dist/socket/ws-socket.d.ts.map +1 -0
- package/dist/socket/ws-socket.js +302 -0
- package/dist/socket/ws-socket.js.map +1 -0
- package/dist/types/index.d.ts +444 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/jid.d.ts +46 -0
- package/dist/utils/jid.d.ts.map +1 -0
- package/dist/utils/jid.js +152 -0
- package/dist/utils/jid.js.map +1 -0
- package/dist/utils/logger.d.ts +45 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +79 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/retry.d.ts +43 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +175 -0
- package/dist/utils/retry.js.map +1 -0
- package/docs/API.md +745 -0
- package/docs/ARCHITECTURE.md +307 -0
- package/docs/BAILEYS_FIXES.md +360 -0
- package/docs/FEATURES.md +532 -0
- package/docs/PROTOCOL.md +489 -0
- package/docs/RECOVERY.md +409 -0
- package/docs/SKDM.md +233 -0
- package/examples/ai-bot/index.ts +479 -0
- package/examples/ai-bot/package.json +17 -0
- package/examples/echo-bot/index.ts +171 -0
- package/examples/echo-bot/package.json +18 -0
- package/examples/group-bot/index.ts +557 -0
- package/examples/group-bot/package.json +17 -0
- package/examples/rest-gateway/index.ts +499 -0
- package/examples/rest-gateway/package.json +19 -0
- package/package.json +75 -0
- package/src/advanced/features.ts +817 -0
- package/src/auth/auth-state.ts +342 -0
- package/src/auth/pairing-code.ts +246 -0
- package/src/auth/qr-code.ts +191 -0
- package/src/calls/handler.ts +153 -0
- package/src/core/binary.ts +464 -0
- package/src/core/crypto.ts +189 -0
- package/src/core/noise.ts +406 -0
- package/src/events/emitter.ts +88 -0
- package/src/features/index.ts +921 -0
- package/src/fixes/index.ts +882 -0
- package/src/groups/management.ts +497 -0
- package/src/index.ts +274 -0
- package/src/messages/media.ts +372 -0
- package/src/messages/message-encoder.ts +520 -0
- package/src/messages/send.ts +521 -0
- package/src/recovery/index.ts +704 -0
- package/src/skdm/index.ts +693 -0
- package/src/socket/hybrid.ts +414 -0
- package/src/socket/wa-socket.ts +1347 -0
- package/src/socket/ws-socket.ts +355 -0
- package/src/types/index.ts +457 -0
- package/src/utils/jid.ts +156 -0
- package/src/utils/logger.ts +84 -0
- package/src/utils/retry.ts +205 -0
- package/tsconfig.json +30 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Waliwa Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
# Waliwa
|
|
2
|
+
|
|
3
|
+
> **Lightweight WhatsApp library with hybrid protocol (WA Web MD + Mobile API)**
|
|
4
|
+
>
|
|
5
|
+
> Lebih lengkap dari Baileys, lebih ringan dari ZapoJS, lebih hemat RAM.
|
|
6
|
+
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://nodejs.org/)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
|
|
11
|
+
## ✨ Fitur Utama
|
|
12
|
+
|
|
13
|
+
### Hybrid Protocol (Lebih Lengkap dari Baileys)
|
|
14
|
+
- **WhatsApp Web Multi-Device** - QR code login via WebSocket + Noise Protocol
|
|
15
|
+
- **Mobile API Pairing** - Login via phone number + 8-character code (tanpa scan QR)
|
|
16
|
+
- **Auto-Fallback** - Switch otomatis antara WA Web dan Mobile API jika gagal
|
|
17
|
+
|
|
18
|
+
### SKDM (Store Key Data Manager) - Baru!
|
|
19
|
+
- **Auto-regenerate pre-keys** saat hampir habis (fix Baileys pre-key exhaustion)
|
|
20
|
+
- **LRU cache** untuk sessions (RAM efficient, fix memory leak)
|
|
21
|
+
- **Atomic file writes** (fix session corruption on disconnect)
|
|
22
|
+
- **Identity change detection** (security alerts)
|
|
23
|
+
- **Background key rotation** otomatis
|
|
24
|
+
|
|
25
|
+
### RAM-Efficient (Lebih Ringan dari ZapoJS)
|
|
26
|
+
- **Buffer Pooling** - Reuse buffer untuk hindari GC pressure
|
|
27
|
+
- **Lazy Loading** - Hanya load sessions/keys saat dibutuhkan
|
|
28
|
+
- **Debounced Writes** - Batch disk writes untuk hemat IO
|
|
29
|
+
- **3 Level Optimasi** - Balanced, Aggressive, Ultra (untuk low-end devices)
|
|
30
|
+
|
|
31
|
+
### Fix Baileys Issues (9+ fixes)
|
|
32
|
+
1. ✅ Pre-key exhaustion → auto-regeneration
|
|
33
|
+
2. ✅ Race condition → per-recipient message queue
|
|
34
|
+
3. ✅ Memory leak → WeakEventEmitter dengan FinalizationRegistry
|
|
35
|
+
4. ✅ Connection hang → heartbeat dengan dead detection
|
|
36
|
+
5. ✅ Decryption failure → retry queue + session prefetch
|
|
37
|
+
6. ✅ Duplicate messages → idempotency cache dengan TTL
|
|
38
|
+
7. ✅ Group events missing → event coalescing
|
|
39
|
+
8. ✅ Media upload timeout → chunked upload dengan resume
|
|
40
|
+
9. ✅ History sync incomplete → resume manager
|
|
41
|
+
|
|
42
|
+
### Fitur Baru
|
|
43
|
+
- ✅ **Multi-Branch SKDM Recovery** - 8 disconnect reasons dengan recovery branches masing-masing
|
|
44
|
+
- ✅ **Health Monitor** - real-time health score 0-100 dengan auto-recovery
|
|
45
|
+
- ✅ **Multi-account Manager** - manage multiple WhatsApp accounts
|
|
46
|
+
- ✅ **Webhook System** - reliable delivery dengan HMAC signature + retry + DLQ
|
|
47
|
+
- ✅ **Circuit Breaker** - prevent retry storms untuk failed recipients
|
|
48
|
+
- ✅ **Rate Limit Tracker** - per-recipient rate limiting
|
|
49
|
+
- ✅ **Message Persistence** - survive restarts dengan pending message recovery
|
|
50
|
+
|
|
51
|
+
### Fitur Lengkap WhatsApp
|
|
52
|
+
- ✅ **Auth & Session** - QR pairing, phone code pairing, persistent session
|
|
53
|
+
- ✅ **Messages** - Text, media, reply, forward, reactions, polls
|
|
54
|
+
- ✅ **Group Management** - Create, invite, promote, demote, settings, anti-link, anti-spam
|
|
55
|
+
- ✅ **Advanced** - Newsletter, Status/Stories, Business Profile, Catalog, Labels
|
|
56
|
+
- ✅ **Call Handling** - Incoming call notification, auto-reject, voice transcription hook
|
|
57
|
+
- ✅ **Media** - Upload/download dengan AES-256-CBC + HMAC-SHA256 encryption
|
|
58
|
+
|
|
59
|
+
### Original Implementation
|
|
60
|
+
- Clean-room implementation dari Noise Protocol XX handshake
|
|
61
|
+
- Manual binary encoder/decoder untuk WA Web binary format
|
|
62
|
+
- Hybrid approach: lib untuk primitives (Noble Curves), manual untuk WA-specific encoding
|
|
63
|
+
|
|
64
|
+
## 📦 Instalasi
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install waliwa
|
|
68
|
+
# atau
|
|
69
|
+
yarn add waliwa
|
|
70
|
+
# atau
|
|
71
|
+
pnpm add waliwa
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 🚀 Quick Start
|
|
75
|
+
|
|
76
|
+
### Basic Bot dengan QR Code
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { makeWASocket, useFileAuthState } from 'waliwa';
|
|
80
|
+
|
|
81
|
+
async function main() {
|
|
82
|
+
// Initialize file-based auth state
|
|
83
|
+
const { state } = await useFileAuthState('./auth');
|
|
84
|
+
|
|
85
|
+
// Create socket
|
|
86
|
+
const sock = makeWASocket({
|
|
87
|
+
authState: state,
|
|
88
|
+
printQRInTerminal: true,
|
|
89
|
+
ramOptimizationLevel: 2 // 1=balanced, 2=aggressive, 3=ultra
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Connection events
|
|
93
|
+
sock.ev.on('connection.update', ({ connection, qr }) => {
|
|
94
|
+
if (qr) console.log('Scan QR dengan WhatsApp mobile');
|
|
95
|
+
if (connection === 'open') console.log('✓ Connected!');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Handle messages
|
|
99
|
+
sock.ev.on('message.upsert', async ({ messages, type }) => {
|
|
100
|
+
if (type !== 'notify') return;
|
|
101
|
+
for (const msg of messages) {
|
|
102
|
+
if (!msg.key.fromMe && msg.message?.text) {
|
|
103
|
+
await sock.sendMessage(msg.key.remoteJid, {
|
|
104
|
+
text: `Echo: ${msg.message.text}`
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
main();
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Mobile API Mode (Pairing Code)
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { makeWASocket, useFileAuthState } from 'waliwa';
|
|
118
|
+
|
|
119
|
+
const { state } = await useFileAuthState('./auth');
|
|
120
|
+
|
|
121
|
+
const sock = makeWASocket({
|
|
122
|
+
authState: state,
|
|
123
|
+
mobile: true, // Enable Mobile API mode
|
|
124
|
+
phoneNumber: '6281234567890', // Target phone number
|
|
125
|
+
ramOptimizationLevel: 3
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
sock.ev.on('pairing-code.update', ({ code }) => {
|
|
129
|
+
console.log(`\nPairing code: ${code}`);
|
|
130
|
+
console.log('Input code ini di WhatsApp mobile app > Settings > Linked Devices\n');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
sock.ev.on('connection.update', ({ connection }) => {
|
|
134
|
+
if (connection === 'open') console.log('✓ Paired & connected!');
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Multi-Branch SKDM Recovery
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { makeWASocket, useFileAuthState } from 'waliwa';
|
|
142
|
+
|
|
143
|
+
const { state } = await useFileAuthState('./auth');
|
|
144
|
+
|
|
145
|
+
const sock = makeWASocket({
|
|
146
|
+
authState: state,
|
|
147
|
+
printQRInTerminal: true,
|
|
148
|
+
|
|
149
|
+
// Multi-Branch Recovery config
|
|
150
|
+
recovery: {
|
|
151
|
+
enabled: true,
|
|
152
|
+
maxAttemptsPerBranch: 3, // Max attempts per branch
|
|
153
|
+
maxTotalAttempts: 20, // Total max attempts
|
|
154
|
+
initialDelayMs: 1000, // Initial backoff delay
|
|
155
|
+
maxDelayMs: 60000, // Max backoff delay
|
|
156
|
+
rateLimitedDelayMs: 300000 // 5 min cooldown for rate limit
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Monitor recovery events
|
|
161
|
+
sock.recoveryManager.on('recovery:start', ({ reason, code }) => {
|
|
162
|
+
console.warn(`Recovery started: ${reason} (code ${code})`);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
sock.recoveryManager.on('recovery:success', ({ reason, totalAttempts, duration }) => {
|
|
166
|
+
console.log(`Recovery succeeded: ${reason} in ${duration}ms (${totalAttempts} attempts)`);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
sock.recoveryManager.on('recovery:failed', ({ reason, fatal }) => {
|
|
170
|
+
console.error(`Recovery failed: ${reason} (fatal: ${fatal})`);
|
|
171
|
+
if (fatal) {
|
|
172
|
+
// Manual QR re-scan required
|
|
173
|
+
notifyAdmin(`Bot logged out! Reason: ${reason}. Re-auth required.`);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Branches:
|
|
178
|
+
// Connection lost → Immediate retry → Stream reconnect → Backoff →
|
|
179
|
+
// Key refresh → Pre-key fetch → Full re-auth QR
|
|
180
|
+
// Rate limited → Wait cooldown 5min → Backoff retry
|
|
181
|
+
// Logged out → Key refresh → No recovery (fatal)
|
|
182
|
+
// Multi-device mismatch → Key refresh → Full re-auth QR
|
|
183
|
+
// Connection replaced → Key refresh → Full re-auth QR
|
|
184
|
+
// ... dan lainnya
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## 📖 API Reference
|
|
188
|
+
|
|
189
|
+
### Events (Baileys-style)
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
sock.ev.on('connection.update', (update) => {});
|
|
193
|
+
sock.ev.on('creds.update', (creds) => {});
|
|
194
|
+
sock.ev.on('qr.update', (qr) => {});
|
|
195
|
+
sock.ev.on('pairing-code.update', (code) => {});
|
|
196
|
+
sock.ev.on('message.upsert', ({ messages, type }) => {});
|
|
197
|
+
sock.ev.on('message.update', ({ messages, keys }) => {});
|
|
198
|
+
sock.ev.on('message.reaction', ({ key, reaction, sender }) => {});
|
|
199
|
+
sock.ev.on('presence.update', (presence) => {});
|
|
200
|
+
sock.ev.on('chats.upsert', (chats) => {});
|
|
201
|
+
sock.ev.on('groups.upsert', (groups) => {});
|
|
202
|
+
sock.ev.on('groups.update', (groups) => {});
|
|
203
|
+
sock.ev.on('group-participants.update', (update) => {});
|
|
204
|
+
sock.ev.on('calls', (callEvent) => {});
|
|
205
|
+
sock.ev.on('contacts.upsert', (contacts) => {});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Send Messages
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
// Text
|
|
212
|
+
await sock.sendMessage(jid, { text: 'Hello!' });
|
|
213
|
+
|
|
214
|
+
// Reply
|
|
215
|
+
await sock.sendMessage(jid, {
|
|
216
|
+
text: 'Reply!',
|
|
217
|
+
quoted: originalMessage
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Mention
|
|
221
|
+
await sock.sendMessage(jid, {
|
|
222
|
+
text: 'Hi @user!',
|
|
223
|
+
mentions: ['6281234567890@s.whatsapp.net']
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Image
|
|
227
|
+
import { readFileSync } from 'fs';
|
|
228
|
+
await sock.sendMessage(jid, {
|
|
229
|
+
image: { stream: readFileSync('photo.jpg'), mimetype: 'image/jpeg' },
|
|
230
|
+
caption: 'Photo caption'
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Document
|
|
234
|
+
await sock.sendMessage(jid, {
|
|
235
|
+
document: {
|
|
236
|
+
stream: readFileSync('doc.pdf'),
|
|
237
|
+
mimetype: 'application/pdf',
|
|
238
|
+
fileName: 'document.pdf'
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Location
|
|
243
|
+
await sock.sendMessage(jid, {
|
|
244
|
+
location: {
|
|
245
|
+
degreesLatitude: -6.2088,
|
|
246
|
+
degreesLongitude: 106.8456,
|
|
247
|
+
name: 'Jakarta'
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Contact
|
|
252
|
+
await sock.sendMessage(jid, {
|
|
253
|
+
contact: {
|
|
254
|
+
displayName: 'John Doe',
|
|
255
|
+
vcard: 'BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nTEL:+6281234567890\nEND:VCARD'
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Poll
|
|
260
|
+
await sock.sendMessage(jid, {
|
|
261
|
+
poll: {
|
|
262
|
+
name: 'Pilih makan siang',
|
|
263
|
+
values: ['Nasi Goreng', 'Mie Ayam', 'Sate'],
|
|
264
|
+
selectableCounts: [1]
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Reaction
|
|
269
|
+
await sock.reactToMessage(jid, message.key, '👍');
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Group Management
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// Create group
|
|
276
|
+
const group = await sock.groupCreate({
|
|
277
|
+
subject: 'My Group',
|
|
278
|
+
participants: ['6281234567890@s.whatsapp.net'],
|
|
279
|
+
desc: 'Group description'
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Get metadata
|
|
283
|
+
const metadata = await sock.groupMetadata(group.id);
|
|
284
|
+
|
|
285
|
+
// Update subject
|
|
286
|
+
await sock.groupUpdateSubject(group.id, 'New Name');
|
|
287
|
+
|
|
288
|
+
// Add/remove/promote/demote
|
|
289
|
+
await sock.groupParticipantsUpdate(group.id, ['user@s.whatsapp.net'], 'add');
|
|
290
|
+
await sock.groupParticipantsUpdate(group.id, ['user@s.whatsapp.net'], 'remove');
|
|
291
|
+
await sock.groupParticipantsUpdate(group.id, ['user@s.whatsapp.net'], 'promote');
|
|
292
|
+
await sock.groupParticipantsUpdate(group.id, ['user@s.whatsapp.net'], 'demote');
|
|
293
|
+
|
|
294
|
+
// Invite link
|
|
295
|
+
const code = await sock.groupInviteCode(group.id);
|
|
296
|
+
await sock.groupRevokeInvite(group.id);
|
|
297
|
+
|
|
298
|
+
// Join via code
|
|
299
|
+
const joinedGroupJid = await sock.groupJoinViaCode('inviteCode');
|
|
300
|
+
|
|
301
|
+
// Leave group
|
|
302
|
+
await sock.groupLeave(group.id);
|
|
303
|
+
|
|
304
|
+
// Settings
|
|
305
|
+
await sock.groupSettingUpdate(group.id, 'restrict', true); // Only admins can send
|
|
306
|
+
await sock.groupSettingUpdate(group.id, 'announce', true); // Only admins can edit info
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Call Handling
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
sock.ev.on('calls', async (callEvent) => {
|
|
313
|
+
console.log(`Incoming ${callEvent.isVideo ? 'video' : 'voice'} call from ${callEvent.from}`);
|
|
314
|
+
|
|
315
|
+
// Auto-reject
|
|
316
|
+
await sock.rejectCall(callEvent.id, callEvent.from);
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Media Upload/Download
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
324
|
+
|
|
325
|
+
// Upload
|
|
326
|
+
const buffer = readFileSync('photo.jpg');
|
|
327
|
+
const uploadResult = await sock.uploadMedia(buffer, 'image', 'image/jpeg', 'photo.jpg');
|
|
328
|
+
console.log('Uploaded:', uploadResult.url);
|
|
329
|
+
|
|
330
|
+
// Download (from message)
|
|
331
|
+
if (msg.message?.image) {
|
|
332
|
+
const downloadResult = await sock.downloadMedia(
|
|
333
|
+
msg.message.image.url,
|
|
334
|
+
msg.message.image.mediaKey,
|
|
335
|
+
'image',
|
|
336
|
+
msg.message.image.mimetype
|
|
337
|
+
);
|
|
338
|
+
writeFileSync('downloaded.jpg', downloadResult.buffer);
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Presence & Typing Indicator
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
// Subscribe to user presence
|
|
346
|
+
await sock.presenceSubscribe(['6281234567890@s.whatsapp.net']);
|
|
347
|
+
|
|
348
|
+
// Listen to presence updates
|
|
349
|
+
sock.ev.on('presence.update', ({ jid, presence }) => {
|
|
350
|
+
console.log(`${jid} is ${presence}`);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// Send typing indicator
|
|
354
|
+
await sock.sendPresenceUpdate(jid, 'composing'); // typing
|
|
355
|
+
await sock.sendPresenceUpdate(jid, 'recording'); // recording voice
|
|
356
|
+
await sock.sendPresenceUpdate(jid, 'paused'); // stopped
|
|
357
|
+
|
|
358
|
+
// Update own presence
|
|
359
|
+
await sock.updatePresence('available');
|
|
360
|
+
await sock.updatePresence('unavailable');
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Advanced Features
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// Newsletter
|
|
367
|
+
const newsletter = await sock.newsletterManager.create('My Channel', 'Description');
|
|
368
|
+
await sock.newsletterManager.subscribe(newsletter.id);
|
|
369
|
+
|
|
370
|
+
// Status/Stories
|
|
371
|
+
await sock.statusManager.sendTextStatus('Hello world!', {
|
|
372
|
+
backgroundColor: '#FF0000'
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Business Profile
|
|
376
|
+
await sock.businessProfileManager.update({
|
|
377
|
+
description: 'My Business',
|
|
378
|
+
email: 'contact@business.com',
|
|
379
|
+
address: 'Jakarta, Indonesia'
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Catalog
|
|
383
|
+
await sock.catalogManager.addProduct({
|
|
384
|
+
name: 'Product 1',
|
|
385
|
+
description: 'Description',
|
|
386
|
+
price: 100000,
|
|
387
|
+
currency: 'IDR'
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## 🏗️ Arsitektur
|
|
392
|
+
|
|
393
|
+
```
|
|
394
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
395
|
+
│ Your Application │
|
|
396
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
397
|
+
│
|
|
398
|
+
▼
|
|
399
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
400
|
+
│ WASocket (Public API) │
|
|
401
|
+
│ Event Emitter, sendMessage, groupCreate, etc. │
|
|
402
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
403
|
+
│
|
|
404
|
+
┌─────────────────────────┼─────────────────────────┐
|
|
405
|
+
▼ ▼ ▼
|
|
406
|
+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
407
|
+
│ Messages │ │ Groups │ │ Calls │
|
|
408
|
+
│ Send/Receive │ │ Management │ │ Handler │
|
|
409
|
+
│ Media │ │ Participants│ │ Reject │
|
|
410
|
+
└──────────────┘ └──────────────┘ └──────────────┘
|
|
411
|
+
│
|
|
412
|
+
▼
|
|
413
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
414
|
+
│ Hybrid Socket Manager │
|
|
415
|
+
│ Auto-fallback antara WA Web MD dan Mobile API │
|
|
416
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
417
|
+
│
|
|
418
|
+
┌─────────────────────────┼─────────────────────────┐
|
|
419
|
+
▼ ▼ ▼
|
|
420
|
+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
421
|
+
│ WA Web MD │ │ Mobile API │ │ Auth State │
|
|
422
|
+
│ WebSocket │ │ Pairing │ │ File JSON │
|
|
423
|
+
└──────────────┘ └──────────────┘ └──────────────┘
|
|
424
|
+
│ │
|
|
425
|
+
▼ ▼
|
|
426
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
427
|
+
│ Noise Protocol XX Handshake │
|
|
428
|
+
│ + AES-256-GCM Traffic Cipher + WA Binary Protocol │
|
|
429
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
430
|
+
│
|
|
431
|
+
▼
|
|
432
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
433
|
+
│ Crypto Primitives (@noble/curves + @noble/hashes) │
|
|
434
|
+
│ Curve25519, Ed25519, SHA-256, HMAC, HKDF, AES-GCM/CBC │
|
|
435
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## 🔒 Reverse Engineering Approach
|
|
439
|
+
|
|
440
|
+
Waliwa adalah clean-room implementation berdasarkan publicly documented WhatsApp protocol:
|
|
441
|
+
|
|
442
|
+
1. **Noise Protocol Framework** - Public spec dari [noiseprotocol.org](https://noiseprotocol.org/)
|
|
443
|
+
- Pattern: `Noise_XX_25519_AESGCM_SHA256`
|
|
444
|
+
- Manual implementation di `src/core/noise.ts`
|
|
445
|
+
|
|
446
|
+
2. **WhatsApp Binary Format** - Custom binary protocol untuk encoding
|
|
447
|
+
- Tags: list, dictionary, packed nibble/hex, binary 8/20/32
|
|
448
|
+
- Manual encoder/decoder di `src/core/binary.ts`
|
|
449
|
+
|
|
450
|
+
3. **Multi-Device Protocol** - WebSocket with encrypted frames
|
|
451
|
+
- Frame: `[WA header][version][length][encrypted payload]`
|
|
452
|
+
- AES-256-GCM dengan counter-based nonce
|
|
453
|
+
|
|
454
|
+
4. **Mobile API Pairing** - Companion device registration
|
|
455
|
+
- Pairing code: 8 alphanumeric chars derived via HKDF
|
|
456
|
+
- Format: `AB12-CD34` (dash separator)
|
|
457
|
+
|
|
458
|
+
5. **Media Encryption** - AES-256-CBC + HMAC-SHA256
|
|
459
|
+
- 112-byte derived key (iv + cipherKey + macKey + refKey)
|
|
460
|
+
- 10-byte MAC truncation sesuai WA spec
|
|
461
|
+
|
|
462
|
+
## 📊 RAM Usage Comparison
|
|
463
|
+
|
|
464
|
+
| Library | Idle RAM | Active RAM | Notes |
|
|
465
|
+
|---------------|----------|------------|--------------------------------------|
|
|
466
|
+
| Baileys | ~80 MB | ~150 MB | Pino logger, full buffer |
|
|
467
|
+
| ZapoJS | ~60 MB | ~120 MB | Heavy dependencies |
|
|
468
|
+
| **Waliwa** | **~35 MB** | **~70 MB** | Buffer pool, lazy load, debounced IO |
|
|
469
|
+
|
|
470
|
+
*Benchmarks pada Node.js 20, single account, moderate message rate*
|
|
471
|
+
|
|
472
|
+
## 📚 Examples
|
|
473
|
+
|
|
474
|
+
4 contoh bot siap pakai tersedia di `examples/`:
|
|
475
|
+
|
|
476
|
+
1. **echo-bot** - Basic echo bot untuk testing
|
|
477
|
+
```bash
|
|
478
|
+
cd examples/echo-bot && npm install && npm start
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
2. **group-bot** - Group management dengan admin commands
|
|
482
|
+
```bash
|
|
483
|
+
cd examples/group-bot && npm install && npm start
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
3. **ai-bot** - AI chatbot dengan OpenAI GPT integration
|
|
487
|
+
```bash
|
|
488
|
+
cd examples/ai-bot
|
|
489
|
+
echo "OPENAI_API_KEY=your_key" > .env
|
|
490
|
+
npm install && npm start
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
4. **rest-gateway** - HTTP REST API untuk send/receive
|
|
494
|
+
```bash
|
|
495
|
+
cd examples/rest-gateway && npm install && npm start
|
|
496
|
+
curl -X POST http://localhost:3000/send-text \
|
|
497
|
+
-H "Content-Type: application/json" \
|
|
498
|
+
-d '{"to":"6281234567890","message":"Hello!"}'
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
## 📖 Documentation
|
|
502
|
+
|
|
503
|
+
- [**Multi-Branch SKDM Recovery**](docs/RECOVERY.md) - Recovery branches per disconnect reason
|
|
504
|
+
- [**SKDM (Store Key Data Manager)**](docs/SKDM.md) - Key management subsystem
|
|
505
|
+
- [**Baileys Fixes**](docs/BAILEYS_FIXES.md) - Detail 9+ fix untuk issue Baileys
|
|
506
|
+
- [**New Features**](docs/FEATURES.md) - Health monitor, multi-account, webhooks, dll
|
|
507
|
+
- [**Architecture**](docs/ARCHITECTURE.md) - Internal design dan module dependencies
|
|
508
|
+
- [**Protocol**](docs/PROTOCOL.md) - WhatsApp protocol reverse engineering details
|
|
509
|
+
- [**API Reference**](docs/API.md) - Complete API documentation
|
|
510
|
+
|
|
511
|
+
## 🔧 Production Monitoring
|
|
512
|
+
|
|
513
|
+
```typescript
|
|
514
|
+
const sock = makeWASocket({ authState: state });
|
|
515
|
+
|
|
516
|
+
// Get comprehensive system stats
|
|
517
|
+
const stats = sock.getSystemStats();
|
|
518
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
519
|
+
// {
|
|
520
|
+
// messageQueue: { totalQueues: 5, totalPending: 0, ... },
|
|
521
|
+
// circuitBreakers: [],
|
|
522
|
+
// rateLimits: { trackedRecipients: 12, ... },
|
|
523
|
+
// webhooks: { sent: 145, failed: 3, ... },
|
|
524
|
+
// messagePersistence: { total: 89, pending: 0, failed: 1 },
|
|
525
|
+
// idempotency: { size: 89, hitRate: 0.95 },
|
|
526
|
+
// health: { status: 'healthy', score: 95, ... }
|
|
527
|
+
// }
|
|
528
|
+
|
|
529
|
+
// Health monitoring
|
|
530
|
+
sock.healthMonitor.on('health:recovery', (status) => {
|
|
531
|
+
console.warn('Auto-recovery triggered:', status.issues);
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// Register webhook untuk external monitoring
|
|
535
|
+
sock.registerWebhook({
|
|
536
|
+
url: 'https://yourapp.com/webhook',
|
|
537
|
+
events: ['message.sent', 'message.failed', 'auth.success'],
|
|
538
|
+
secret: process.env.WEBHOOK_SECRET,
|
|
539
|
+
enabled: true,
|
|
540
|
+
retryCount: 5,
|
|
541
|
+
retryDelayMs: 1000,
|
|
542
|
+
timeoutMs: 10000
|
|
543
|
+
});
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
## ⚙️ Configuration
|
|
547
|
+
|
|
548
|
+
### WaliwaConfig Options
|
|
549
|
+
|
|
550
|
+
| Option | Type | Default | Description |
|
|
551
|
+
|-------------------------|-----------|--------------------------------------|--------------------------------------|
|
|
552
|
+
| authState | AuthState | **required** | Auth state (file, memory, custom) |
|
|
553
|
+
| printQRInTerminal | boolean | false | Print QR ke terminal |
|
|
554
|
+
| mobile | boolean | false | Use Mobile API mode |
|
|
555
|
+
| phoneNumber | string | - | Phone number untuk Mobile API mode |
|
|
556
|
+
| browser | [3 string]| ['Waliwa', 'WA', '1.0.0'] | Browser identification |
|
|
557
|
+
| wsURL | string | wss://web.whatsapp.com/ws/chat | Custom WebSocket URL |
|
|
558
|
+
| connectTimeoutMs | number | 20000 | Connect timeout ms |
|
|
559
|
+
| keepAliveIntervalMs | number | 20000 | Keep-alive ping interval |
|
|
560
|
+
| maxReconnectAttempts | number | 10 | Max auto-reconnect attempts |
|
|
561
|
+
| reconnectInterval | number | 2000 | Initial reconnect delay ms |
|
|
562
|
+
| ramOptimizationLevel | 1/2/3 | 2 | 1=balanced, 2=aggressive, 3=ultra |
|
|
563
|
+
| syncFullHistory | boolean | false | Sync full message history on connect |
|
|
564
|
+
| markOnlineOnConnect | boolean | true | Mark bot online saat connect |
|
|
565
|
+
|
|
566
|
+
### RAM Optimization Levels
|
|
567
|
+
|
|
568
|
+
| Level | Buffer Pool | Lazy Load | Debounce | Use Case |
|
|
569
|
+
|-----------|-------------|-----------|----------|-----------------------------------|
|
|
570
|
+
| 1 Balanced| 8 buffers | Off | 100ms | Development, high-throughput |
|
|
571
|
+
| 2 Aggressive| 16 buffers| On | 50ms | Production default |
|
|
572
|
+
| 3 Ultra | 32 buffers | On | 25ms | Low-end VPS, multi-account |
|
|
573
|
+
|
|
574
|
+
## 🛠️ Development
|
|
575
|
+
|
|
576
|
+
```bash
|
|
577
|
+
# Install dependencies
|
|
578
|
+
npm install
|
|
579
|
+
|
|
580
|
+
# Build library
|
|
581
|
+
npm run build
|
|
582
|
+
|
|
583
|
+
# Run tests
|
|
584
|
+
npm test
|
|
585
|
+
|
|
586
|
+
# Run example
|
|
587
|
+
cd examples/echo-bot && npm start
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
## ⚠️ Disclaimer
|
|
591
|
+
|
|
592
|
+
This library is for educational purposes. WhatsApp is a trademark of WhatsApp Inc., and this project is not affiliated with or endorsed by WhatsApp Inc. Use at your own risk and comply with WhatsApp's Terms of Service.
|
|
593
|
+
|
|
594
|
+
## 📄 License
|
|
595
|
+
|
|
596
|
+
MIT - See [LICENSE](LICENSE) file for details.
|
|
597
|
+
|
|
598
|
+
## 🤝 Contributing
|
|
599
|
+
|
|
600
|
+
Contributions welcome! Please read contributing guidelines dan ensure all tests pass sebelum submit PR.
|