@kangwifi-pro/waliwa 3.0.0 → 3.0.1
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 +355 -488
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/docs/API.md +311 -0
- package/docs/EXAMPLES.md +204 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,600 +1,467 @@
|
|
|
1
1
|
# Waliwa
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> WhatsApp library based on Baileys 7 + zaileys + baileys-mbuilder
|
|
4
4
|
>
|
|
5
|
-
>
|
|
5
|
+
> Super ringan, fluent API, dengan MessageBuilder, CommandSystem, Automation, dan Plugin system.
|
|
6
6
|
|
|
7
|
-
[](https://nodejs.org/)
|
|
7
|
+
[](https://www.npmjs.com/package/@kangwifi-pro/waliwa)
|
|
9
8
|
[](LICENSE)
|
|
10
9
|
|
|
11
|
-
##
|
|
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
|
|
10
|
+
## Instalasi
|
|
65
11
|
|
|
66
12
|
```bash
|
|
67
|
-
npm install waliwa
|
|
68
|
-
# atau
|
|
69
|
-
yarn add waliwa
|
|
70
|
-
# atau
|
|
71
|
-
pnpm add waliwa
|
|
13
|
+
npm install @kangwifi-pro/waliwa
|
|
72
14
|
```
|
|
73
15
|
|
|
74
|
-
##
|
|
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)
|
|
16
|
+
## Quick Start (3 baris)
|
|
115
17
|
|
|
116
18
|
```typescript
|
|
117
|
-
import {
|
|
118
|
-
|
|
119
|
-
const { state } = await useFileAuthState('./auth');
|
|
19
|
+
import { quickStart, MessageHelper } from '@kangwifi-pro/waliwa';
|
|
120
20
|
|
|
121
|
-
const sock =
|
|
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
|
-
});
|
|
21
|
+
const sock = await quickStart({ authFolder: './auth' });
|
|
132
22
|
|
|
133
|
-
sock.ev.on('
|
|
134
|
-
|
|
23
|
+
sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
24
|
+
for (const msg of messages) {
|
|
25
|
+
if (MessageHelper.isFromMe(msg)) continue;
|
|
26
|
+
const text = MessageHelper.getText(msg);
|
|
27
|
+
if (text === '!ping') await MessageHelper.reply(sock, msg, 'pong! 🏓');
|
|
28
|
+
}
|
|
135
29
|
});
|
|
136
30
|
```
|
|
137
31
|
|
|
138
|
-
|
|
32
|
+
QR otomatis muncul di terminal. Scan dengan WhatsApp → Settings → Linked Devices.
|
|
139
33
|
|
|
140
|
-
|
|
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
|
-
});
|
|
34
|
+
Auto-reconnect, auto-save credentials, auto-keepalive — semua sudah aktif default.
|
|
159
35
|
|
|
160
|
-
|
|
161
|
-
sock.recoveryManager.on('recovery:start', ({ reason, code }) => {
|
|
162
|
-
console.warn(`Recovery started: ${reason} (code ${code})`);
|
|
163
|
-
});
|
|
36
|
+
## Fitur Utama
|
|
164
37
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
// ... dan lainnya
|
|
185
|
-
```
|
|
38
|
+
| Modul | Asal | Deskripsi |
|
|
39
|
+
|-------|------|-----------|
|
|
40
|
+
| Core | Baileys 7 | Koneksi WA Web multi-device, Noise XX, protobuf |
|
|
41
|
+
| `quickStart()` | Waliwa | One-liner setup dengan auto QR, reconnect, save creds |
|
|
42
|
+
| `MessageBuilder` | zaileys | Fluent chaining: `.to().text().image().reply().send()` |
|
|
43
|
+
| `ButtonBuilder` | baileys-mbuilder | Button interactive messages |
|
|
44
|
+
| `CarouselBuilder` | baileys-mbuilder | Carousel card messages |
|
|
45
|
+
| `AIRichBuilder` | baileys-mbuilder | AI rich response (hyperlink, citation, LaTeX) |
|
|
46
|
+
| `CommandSystem` | zaileys | Command registry + middleware + guards (cooldown, adminOnly) |
|
|
47
|
+
| `AutoRejectCall` | zaileys | Auto-reject incoming calls |
|
|
48
|
+
| `AutoRead` | zaileys | Auto-mark messages as read |
|
|
49
|
+
| `AutoPresence` | zaileys | Auto typing indicator |
|
|
50
|
+
| `Broadcast` | zaileys | Send to multiple recipients dengan delay |
|
|
51
|
+
| `Scheduler` | zaileys | Schedule messages untuk future delivery |
|
|
52
|
+
| `RateLimiter` | zaileys | Per-recipient rate limiting |
|
|
53
|
+
| `PluginRegistry` | zaileys | Modular plugin system dengan `definePlugin()` |
|
|
54
|
+
| `MediaHelper` | Waliwa | Download, convert, resize (sharp) |
|
|
55
|
+
| `StickerHelper` | Waliwa | Create stickers dari image/video/text (sharp + ffmpeg) |
|
|
56
|
+
| `MessageHelper` | Waliwa | Parse, format, reply utilities |
|
|
186
57
|
|
|
187
|
-
##
|
|
58
|
+
## API
|
|
188
59
|
|
|
189
|
-
###
|
|
60
|
+
### quickStart()
|
|
190
61
|
|
|
191
62
|
```typescript
|
|
192
|
-
sock
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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) => {});
|
|
63
|
+
const sock = await quickStart({
|
|
64
|
+
authFolder: './auth',
|
|
65
|
+
browser: ['Waliwa Bot', 'Chrome', '1.0.0'],
|
|
66
|
+
printQRInTerminal: true, // default: true
|
|
67
|
+
autoReconnect: true, // default: true
|
|
68
|
+
reconnectDelayMs: 5000, // default: 5000
|
|
69
|
+
markOnlineOnConnect: true,
|
|
70
|
+
syncFullHistory: false
|
|
71
|
+
});
|
|
206
72
|
```
|
|
207
73
|
|
|
208
|
-
###
|
|
74
|
+
### MessageBuilder
|
|
209
75
|
|
|
210
76
|
```typescript
|
|
211
|
-
|
|
212
|
-
await sock.sendMessage(jid, { text: 'Hello!' });
|
|
77
|
+
import { MessageBuilder } from '@kangwifi-pro/waliwa';
|
|
213
78
|
|
|
214
|
-
|
|
215
|
-
await sock.sendMessage(jid, {
|
|
216
|
-
text: 'Reply!',
|
|
217
|
-
quoted: originalMessage
|
|
218
|
-
});
|
|
79
|
+
const builder = new MessageBuilder(sock);
|
|
219
80
|
|
|
220
|
-
//
|
|
221
|
-
await
|
|
222
|
-
text: 'Hi @user!',
|
|
223
|
-
mentions: ['6281234567890@s.whatsapp.net']
|
|
224
|
-
});
|
|
81
|
+
// Text + reply + mentions
|
|
82
|
+
await builder.to(jid).text('Hello!').reply(message).mentions(['user@s.whatsapp.net']).send();
|
|
225
83
|
|
|
226
84
|
// Image
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
});
|
|
85
|
+
await builder.to(jid).image(buffer, { caption: 'Check this!' }).send();
|
|
86
|
+
|
|
87
|
+
// Audio (voice note)
|
|
88
|
+
await builder.to(jid).audio(buffer, { ptt: true }).send();
|
|
232
89
|
|
|
233
90
|
// Document
|
|
234
|
-
await
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
fileName: 'document.pdf'
|
|
239
|
-
}
|
|
240
|
-
});
|
|
91
|
+
await builder.to(jid).document(buffer, { fileName: 'report.pdf' }).send();
|
|
92
|
+
|
|
93
|
+
// Sticker
|
|
94
|
+
await builder.to(jid).sticker(webpBuffer).send();
|
|
241
95
|
|
|
242
96
|
// Location
|
|
243
|
-
await
|
|
244
|
-
location: {
|
|
245
|
-
degreesLatitude: -6.2088,
|
|
246
|
-
degreesLongitude: 106.8456,
|
|
247
|
-
name: 'Jakarta'
|
|
248
|
-
}
|
|
249
|
-
});
|
|
97
|
+
await builder.to(jid).location(-6.2088, 106.8456, { name: 'Jakarta' }).send();
|
|
250
98
|
|
|
251
99
|
// Contact
|
|
252
|
-
await
|
|
253
|
-
contact: {
|
|
254
|
-
displayName: 'John Doe',
|
|
255
|
-
vcard: 'BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nTEL:+6281234567890\nEND:VCARD'
|
|
256
|
-
}
|
|
257
|
-
});
|
|
100
|
+
await builder.to(jid).contact(vcardString, 'John Doe').send();
|
|
258
101
|
|
|
259
102
|
// Poll
|
|
260
|
-
await
|
|
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
|
-
```
|
|
103
|
+
await builder.to(jid).poll('Pilih makan?', ['Nasi', 'Mie', 'Sate']).send();
|
|
271
104
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
subject: 'My Group',
|
|
278
|
-
participants: ['6281234567890@s.whatsapp.net'],
|
|
279
|
-
desc: 'Group description'
|
|
280
|
-
});
|
|
105
|
+
// Buttons
|
|
106
|
+
await builder.to(jid).text('Choose:').buttons([
|
|
107
|
+
{ label: 'Yes', id: 'yes' },
|
|
108
|
+
{ label: 'No', id: 'no' }
|
|
109
|
+
]).send();
|
|
281
110
|
|
|
282
|
-
//
|
|
283
|
-
|
|
111
|
+
// Carousel
|
|
112
|
+
await builder.to(jid).carousel([
|
|
113
|
+
{ title: 'Product A', text: '$10', buttons: [{ label: 'Buy', id: 'buy_a' }] },
|
|
114
|
+
{ title: 'Product B', text: '$20', buttons: [{ label: 'Buy', id: 'buy_b' }] }
|
|
115
|
+
]).send();
|
|
284
116
|
|
|
285
|
-
//
|
|
286
|
-
await
|
|
117
|
+
// Broadcast ke multiple recipients
|
|
118
|
+
await builder.to('dummy').text('Announcement!').broadcast([jid1, jid2, jid3], 2000);
|
|
287
119
|
|
|
288
|
-
//
|
|
289
|
-
await
|
|
290
|
-
|
|
291
|
-
await sock.groupParticipantsUpdate(group.id, ['user@s.whatsapp.net'], 'promote');
|
|
292
|
-
await sock.groupParticipantsUpdate(group.id, ['user@s.whatsapp.net'], 'demote');
|
|
120
|
+
// View once
|
|
121
|
+
await builder.to(jid).image(buffer).viewOnce().send();
|
|
122
|
+
```
|
|
293
123
|
|
|
294
|
-
|
|
295
|
-
const code = await sock.groupInviteCode(group.id);
|
|
296
|
-
await sock.groupRevokeInvite(group.id);
|
|
124
|
+
### ButtonBuilder
|
|
297
125
|
|
|
298
|
-
|
|
299
|
-
|
|
126
|
+
```typescript
|
|
127
|
+
import { ButtonBuilder } from '@kangwifi-pro/waliwa';
|
|
300
128
|
|
|
301
|
-
|
|
302
|
-
|
|
129
|
+
const msg = new ButtonBuilder()
|
|
130
|
+
.text('Choose an option:')
|
|
131
|
+
.button('Yes', 'yes_id')
|
|
132
|
+
.button('No', 'no_id')
|
|
133
|
+
.footer('Powered by Waliwa')
|
|
134
|
+
.build();
|
|
303
135
|
|
|
304
|
-
|
|
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
|
|
136
|
+
await sock.sendMessage(jid, msg);
|
|
307
137
|
```
|
|
308
138
|
|
|
309
|
-
###
|
|
139
|
+
### CarouselBuilder
|
|
310
140
|
|
|
311
141
|
```typescript
|
|
312
|
-
|
|
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
|
-
```
|
|
142
|
+
import { CarouselBuilder } from '@kangwifi-pro/waliwa';
|
|
319
143
|
|
|
320
|
-
|
|
144
|
+
const msg = new CarouselBuilder()
|
|
145
|
+
.card(c => c.title('Product A').text('$10').button('Buy', 'buy_a'))
|
|
146
|
+
.card(c => c.title('Product B').text('$20').button('Buy', 'buy_b'))
|
|
147
|
+
.build();
|
|
321
148
|
|
|
322
|
-
|
|
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
|
-
}
|
|
149
|
+
await sock.sendMessage(jid, msg);
|
|
340
150
|
```
|
|
341
151
|
|
|
342
|
-
###
|
|
152
|
+
### AIRichBuilder
|
|
343
153
|
|
|
344
154
|
```typescript
|
|
345
|
-
|
|
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
|
-
});
|
|
155
|
+
import { AIRichBuilder } from '@kangwifi-pro/waliwa';
|
|
352
156
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
await sock.sendPresenceUpdate(jid, 'paused'); // stopped
|
|
157
|
+
const msg = new AIRichBuilder()
|
|
158
|
+
.text('Visit [Google](https://google.com) for details')
|
|
159
|
+
.build();
|
|
357
160
|
|
|
358
|
-
|
|
359
|
-
await sock.updatePresence('available');
|
|
360
|
-
await sock.updatePresence('unavailable');
|
|
161
|
+
await sock.sendMessage(jid, msg);
|
|
361
162
|
```
|
|
362
163
|
|
|
363
|
-
###
|
|
164
|
+
### CommandSystem
|
|
364
165
|
|
|
365
166
|
```typescript
|
|
366
|
-
|
|
367
|
-
const newsletter = await sock.newsletterManager.create('My Channel', 'Description');
|
|
368
|
-
await sock.newsletterManager.subscribe(newsletter.id);
|
|
167
|
+
import { CommandSystem } from '@kangwifi-pro/waliwa';
|
|
369
168
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
169
|
+
const cmd = new CommandSystem(sock, {
|
|
170
|
+
prefix: '!',
|
|
171
|
+
ownerIds: ['6281234567890@s.whatsapp.net']
|
|
373
172
|
});
|
|
374
173
|
|
|
375
|
-
//
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
email: 'contact@business.com',
|
|
379
|
-
address: 'Jakarta, Indonesia'
|
|
174
|
+
// Simple command
|
|
175
|
+
cmd.command('ping', async (ctx) => {
|
|
176
|
+
await ctx.reply('pong! 🏓');
|
|
380
177
|
});
|
|
381
178
|
|
|
382
|
-
//
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
179
|
+
// With guards
|
|
180
|
+
cmd.command('kick', {
|
|
181
|
+
adminOnly: true,
|
|
182
|
+
cooldown: 5000,
|
|
183
|
+
description: 'Kick member dari grup',
|
|
184
|
+
aliases: ['remove']
|
|
185
|
+
}, async (ctx) => {
|
|
186
|
+
// ctx.sender, ctx.jid, ctx.args, ctx.isGroup, ctx.isOwner, ctx.quoted
|
|
187
|
+
const target = ctx.mentions[0] || ctx.quoted?.participant;
|
|
188
|
+
if (!target) return ctx.reply('Tag atau reply user yang mau di-kick');
|
|
189
|
+
await sock.groupParticipantsUpdate(ctx.jid, [target], 'remove');
|
|
190
|
+
await ctx.reply(`✅ Kicked ${target}`);
|
|
388
191
|
});
|
|
389
|
-
```
|
|
390
192
|
|
|
391
|
-
|
|
193
|
+
// Middleware
|
|
194
|
+
cmd.use(async (ctx, next) => {
|
|
195
|
+
console.log(`[${ctx.senderName}] ${ctx.fullText}`);
|
|
196
|
+
await next();
|
|
197
|
+
});
|
|
392
198
|
|
|
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
|
-
└──────────────────────────────────────────────────────────────────┘
|
|
199
|
+
cmd.attach();
|
|
436
200
|
```
|
|
437
201
|
|
|
438
|
-
|
|
202
|
+
#### CommandContext Properties
|
|
203
|
+
|
|
204
|
+
| Property | Type | Description |
|
|
205
|
+
|----------|------|-------------|
|
|
206
|
+
| `sock` | any | Socket instance |
|
|
207
|
+
| `message` | any | Raw WA message |
|
|
208
|
+
| `jid` | string | Chat JID |
|
|
209
|
+
| `sender` | string | Sender JID |
|
|
210
|
+
| `text` | string | Full message text |
|
|
211
|
+
| `command` | string | Command name (tanpa prefix) |
|
|
212
|
+
| `args` | string[] | Arguments |
|
|
213
|
+
| `isGroup` | boolean | Apakah dari grup |
|
|
214
|
+
| `isFromMe` | boolean | Apakah dari bot sendiri |
|
|
215
|
+
| `isOwner` | boolean | Apakah sender adalah owner |
|
|
216
|
+
| `senderName` | string | Nama sender (pushName) |
|
|
217
|
+
| `quoted` | any | Quoted message context |
|
|
218
|
+
| `mentions` | string[] | Mentioned JIDs |
|
|
219
|
+
|
|
220
|
+
#### CommandContext Methods
|
|
221
|
+
|
|
222
|
+
| Method | Description |
|
|
223
|
+
|--------|-------------|
|
|
224
|
+
| `reply(text)` | Reply ke message |
|
|
225
|
+
| `replyWithMedia(buffer)` | Reply dengan image |
|
|
226
|
+
| `react(emoji)` | React ke message |
|
|
227
|
+
| `sendTyping()` | Kirim typing indicator |
|
|
228
|
+
| `sendRecording()` | Kirim recording indicator |
|
|
229
|
+
| `stopTyping()` | Stop typing indicator |
|
|
230
|
+
|
|
231
|
+
#### CommandOptions
|
|
232
|
+
|
|
233
|
+
| Option | Type | Description |
|
|
234
|
+
|--------|------|-------------|
|
|
235
|
+
| `adminOnly` | boolean | Hanya admin grup |
|
|
236
|
+
| `ownerOnly` | boolean | Hanya owner bot |
|
|
237
|
+
| `groupOnly` | boolean | Hanya bisa di grup |
|
|
238
|
+
| `privateOnly` | boolean | Hanya bisa di DM |
|
|
239
|
+
| `cooldown` | number | Cooldown dalam ms |
|
|
240
|
+
| `rateLimit` | { max, windowMs } | Rate limit per user |
|
|
241
|
+
| `aliases` | string[] | Command aliases |
|
|
242
|
+
| `description` | string | Deskripsi untuk help |
|
|
243
|
+
|
|
244
|
+
### Automation
|
|
439
245
|
|
|
440
|
-
|
|
246
|
+
```typescript
|
|
247
|
+
import { AutoRejectCall, AutoRead, AutoPresence, Broadcast, Scheduler, RateLimiter } from '@kangwifi-pro/waliwa';
|
|
248
|
+
|
|
249
|
+
// Auto-reject incoming calls
|
|
250
|
+
const autoReject = new AutoRejectCall(sock);
|
|
251
|
+
autoReject.start();
|
|
252
|
+
// Allow specific users
|
|
253
|
+
autoReject.allow('6281234567890@s.whatsapp.net');
|
|
254
|
+
|
|
255
|
+
// Auto-read messages (exclude groups)
|
|
256
|
+
const autoRead = new AutoRead(sock, { excludeGroups: true });
|
|
257
|
+
autoRead.start();
|
|
258
|
+
|
|
259
|
+
// Auto typing indicator
|
|
260
|
+
const autoPresence = new AutoPresence(sock);
|
|
261
|
+
autoPresence.start();
|
|
262
|
+
|
|
263
|
+
// Broadcast
|
|
264
|
+
const broadcast = new Broadcast(sock, 2000); // 2s delay
|
|
265
|
+
await broadcast.sendText([jid1, jid2, jid3], 'Announcement!');
|
|
266
|
+
|
|
267
|
+
// Scheduler
|
|
268
|
+
const scheduler = new Scheduler(sock);
|
|
269
|
+
scheduler.schedule('reminder1', jid, { text: 'Waktunya meeting!' }, 60 * 60 * 1000); // 1 hour
|
|
270
|
+
scheduler.cancel('reminder1');
|
|
271
|
+
scheduler.list(); // [{ id, jid }]
|
|
272
|
+
|
|
273
|
+
// Rate limiter
|
|
274
|
+
const limiter = new RateLimiter();
|
|
275
|
+
limiter.setLimit(jid, 10, 60000); // 10 messages per minute
|
|
276
|
+
if (limiter.canSend(jid)) {
|
|
277
|
+
await sock.sendMessage(jid, { text: 'Hi!' });
|
|
278
|
+
}
|
|
279
|
+
```
|
|
441
280
|
|
|
442
|
-
|
|
443
|
-
- Pattern: `Noise_XX_25519_AESGCM_SHA256`
|
|
444
|
-
- Manual implementation di `src/core/noise.ts`
|
|
281
|
+
### Plugin System
|
|
445
282
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
283
|
+
```typescript
|
|
284
|
+
import { definePlugin, PluginLoader } from '@kangwifi-pro/waliwa';
|
|
285
|
+
|
|
286
|
+
// plugins/echo.ts
|
|
287
|
+
export default definePlugin({
|
|
288
|
+
name: 'echo',
|
|
289
|
+
version: '1.0.0',
|
|
290
|
+
description: 'Echo plugin',
|
|
291
|
+
onLoad: (ctx) => console.log('Echo plugin loaded!'),
|
|
292
|
+
onMessage: async (msg, ctx) => {
|
|
293
|
+
const text = msg.message?.conversation || '';
|
|
294
|
+
if (text === '!echo') {
|
|
295
|
+
await ctx.sock.sendMessage(msg.key.remoteJid, { text: 'Echo!' });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
});
|
|
449
299
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
300
|
+
// Load plugins
|
|
301
|
+
const loader = new PluginLoader(sock);
|
|
302
|
+
await loader.loadFromDir('./plugins');
|
|
303
|
+
loader.getRegistry().list(); // [{ name, version, description }]
|
|
304
|
+
```
|
|
453
305
|
|
|
454
|
-
|
|
455
|
-
- Pairing code: 8 alphanumeric chars derived via HKDF
|
|
456
|
-
- Format: `AB12-CD34` (dash separator)
|
|
306
|
+
### MediaHelper
|
|
457
307
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
- 10-byte MAC truncation sesuai WA spec
|
|
308
|
+
```typescript
|
|
309
|
+
import { MediaHelper } from '@kangwifi-pro/waliwa';
|
|
461
310
|
|
|
462
|
-
|
|
311
|
+
// Download dari message
|
|
312
|
+
const buffer = await MediaHelper.downloadFromMessage(sock, msg);
|
|
463
313
|
|
|
464
|
-
|
|
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 |
|
|
314
|
+
// Download dari URL
|
|
315
|
+
const buffer = await MediaHelper.downloadFromUrl('https://...');
|
|
469
316
|
|
|
470
|
-
|
|
317
|
+
// Resize (untuk profile picture 640x640)
|
|
318
|
+
const resized = await MediaHelper.resizeImage(buffer, 640, 640);
|
|
471
319
|
|
|
472
|
-
|
|
320
|
+
// Convert format
|
|
321
|
+
const webp = await MediaHelper.toWebP(buffer);
|
|
322
|
+
const jpeg = await MediaHelper.toJPEG(buffer, 80);
|
|
473
323
|
|
|
474
|
-
|
|
324
|
+
// Compress
|
|
325
|
+
const compressed = await MediaHelper.compress(buffer, 60);
|
|
475
326
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
cd examples/echo-bot && npm install && npm start
|
|
479
|
-
```
|
|
327
|
+
// Thumbnail
|
|
328
|
+
const thumb = await MediaHelper.createThumbnail(buffer, 200);
|
|
480
329
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
cd examples/group-bot && npm install && npm start
|
|
484
|
-
```
|
|
330
|
+
// Watermark
|
|
331
|
+
const watermarked = await MediaHelper.addWatermark(buffer, '© Waliwa');
|
|
485
332
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
npm install && npm start
|
|
491
|
-
```
|
|
333
|
+
// Grayscale / blur
|
|
334
|
+
const gray = await MediaHelper.grayscale(buffer);
|
|
335
|
+
const blurred = await MediaHelper.blur(buffer, 5);
|
|
336
|
+
```
|
|
492
337
|
|
|
493
|
-
|
|
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
|
-
```
|
|
338
|
+
### StickerHelper
|
|
500
339
|
|
|
501
|
-
|
|
340
|
+
```typescript
|
|
341
|
+
import { StickerHelper } from '@kangwifi-pro/waliwa';
|
|
502
342
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
- [**API Reference**](docs/API.md) - Complete API documentation
|
|
343
|
+
// Dari image
|
|
344
|
+
const sticker = await StickerHelper.fromImage(imageBuffer, {
|
|
345
|
+
pack: 'My Pack',
|
|
346
|
+
author: 'Me',
|
|
347
|
+
categories: ['😀']
|
|
348
|
+
});
|
|
510
349
|
|
|
511
|
-
|
|
350
|
+
// Dari video (animated)
|
|
351
|
+
const animated = await StickerHelper.fromVideo(videoBuffer);
|
|
512
352
|
|
|
513
|
-
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
|
|
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);
|
|
353
|
+
// Dari text
|
|
354
|
+
const textSticker = await StickerHelper.fromText('Hello!', {
|
|
355
|
+
color: '#ffffff',
|
|
356
|
+
backgroundColor: '#000000'
|
|
532
357
|
});
|
|
533
358
|
|
|
534
|
-
//
|
|
535
|
-
|
|
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
|
-
```
|
|
359
|
+
// Dari URL
|
|
360
|
+
const urlSticker = await StickerHelper.fromUrl('https://...');
|
|
545
361
|
|
|
546
|
-
|
|
362
|
+
// Dari file
|
|
363
|
+
const fileSticker = await StickerHelper.fromFile('./image.jpg');
|
|
547
364
|
|
|
548
|
-
|
|
365
|
+
// Send
|
|
366
|
+
await sock.sendMessage(jid, { sticker });
|
|
367
|
+
```
|
|
549
368
|
|
|
550
|
-
|
|
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 |
|
|
369
|
+
### MessageHelper
|
|
565
370
|
|
|
566
|
-
|
|
371
|
+
```typescript
|
|
372
|
+
import { MessageHelper } from '@kangwifi-pro/waliwa';
|
|
373
|
+
|
|
374
|
+
// Extract text
|
|
375
|
+
const text = MessageHelper.getText(msg);
|
|
376
|
+
|
|
377
|
+
// Info
|
|
378
|
+
MessageHelper.isFromMe(msg);
|
|
379
|
+
MessageHelper.isGroupMessage(msg);
|
|
380
|
+
MessageHelper.getSender(msg);
|
|
381
|
+
MessageHelper.getChat(msg);
|
|
382
|
+
MessageHelper.hasMedia(msg);
|
|
383
|
+
MessageHelper.getMediaType(msg);
|
|
384
|
+
MessageHelper.getMentions(msg);
|
|
385
|
+
MessageHelper.getQuoted(msg);
|
|
386
|
+
|
|
387
|
+
// Parse command
|
|
388
|
+
const cmd = MessageHelper.parseCommand(msg, '!');
|
|
389
|
+
// { command: 'ping', args: [], fullText: '!ping' }
|
|
390
|
+
|
|
391
|
+
// Actions
|
|
392
|
+
await MessageHelper.reply(sock, msg, 'Hello!');
|
|
393
|
+
await MessageHelper.sendText(sock, jid, 'Hi!', { mentions: [...] });
|
|
394
|
+
await MessageHelper.mentionAll(sock, groupJid);
|
|
395
|
+
await MessageHelper.react(sock, msg, '👍');
|
|
396
|
+
await MessageHelper.markAsRead(sock, msg.key);
|
|
397
|
+
await MessageHelper.sendTyping(sock, jid);
|
|
398
|
+
await MessageHelper.stopTyping(sock, jid);
|
|
399
|
+
await MessageHelper.delete(sock, msg);
|
|
400
|
+
await MessageHelper.forward(sock, toJid, msg);
|
|
401
|
+
```
|
|
567
402
|
|
|
568
|
-
|
|
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 |
|
|
403
|
+
## Events
|
|
573
404
|
|
|
574
|
-
|
|
405
|
+
```typescript
|
|
406
|
+
sock.ev.on('connection.update', (update) => {
|
|
407
|
+
// update.connection: 'connecting' | 'open' | 'close'
|
|
408
|
+
// update.qr: string (QR code)
|
|
409
|
+
// update.lastDisconnect: { error, output }
|
|
410
|
+
});
|
|
575
411
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
412
|
+
sock.ev.on('messages.upsert', ({ messages, type }) => {
|
|
413
|
+
// type: 'notify' (new) | 'append' (history)
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
sock.ev.on('messages.update', (updates) => {
|
|
417
|
+
// Status updates: sent, delivered, read
|
|
418
|
+
});
|
|
579
419
|
|
|
580
|
-
|
|
581
|
-
|
|
420
|
+
sock.ev.on('message-receipt.update', (updates) => {
|
|
421
|
+
// Delivery/read receipts
|
|
422
|
+
});
|
|
582
423
|
|
|
583
|
-
|
|
584
|
-
|
|
424
|
+
sock.ev.on('presence.update', ({ id, presences }) => {
|
|
425
|
+
// Presence: available, unavailable, composing, recording
|
|
426
|
+
});
|
|
585
427
|
|
|
586
|
-
|
|
587
|
-
|
|
428
|
+
sock.ev.on('chats.upsert', (chats) => {});
|
|
429
|
+
sock.ev.on('chats.update', (chats) => {});
|
|
430
|
+
sock.ev.on('contacts.upsert', (contacts) => {});
|
|
431
|
+
sock.ev.on('groups.upsert', (groups) => {});
|
|
432
|
+
sock.ev.on('groups.update', (groups) => {});
|
|
433
|
+
sock.ev.on('group-participants.update', (update) => {
|
|
434
|
+
// update.action: 'add' | 'remove' | 'promote' | 'demote'
|
|
435
|
+
});
|
|
436
|
+
sock.ev.on('call', (calls) => {});
|
|
437
|
+
sock.ev.on('creds.update', () => saveCreds());
|
|
588
438
|
```
|
|
589
439
|
|
|
590
|
-
##
|
|
440
|
+
## Reconnect Behavior
|
|
441
|
+
|
|
442
|
+
| Status Code | Reason | Reconnect? |
|
|
443
|
+
|-------------|--------|------------|
|
|
444
|
+
| 515 | Restart required | ✅ Ya |
|
|
445
|
+
| 428 | Connection closed | ✅ Ya |
|
|
446
|
+
| 440 | Connection replaced | ✅ Ya |
|
|
447
|
+
| 500 | Server error | ✅ Ya |
|
|
448
|
+
| 401 | Logged out | ❌ Tidak (clear auth folder) |
|
|
591
449
|
|
|
592
|
-
|
|
450
|
+
Auto-reconnect default aktif dengan delay 5 detik.
|
|
593
451
|
|
|
594
|
-
##
|
|
452
|
+
## Dependencies
|
|
595
453
|
|
|
596
|
-
|
|
454
|
+
| Package | Purpose |
|
|
455
|
+
|---------|---------|
|
|
456
|
+
| `@whiskeysockets/baileys` | Core WA protocol |
|
|
457
|
+
| `@hapi/boom` | Error handling |
|
|
458
|
+
| `pino` + `pino-pretty` | Logging |
|
|
459
|
+
| `qrcode-terminal` | QR display |
|
|
460
|
+
| `axios` | HTTP requests |
|
|
461
|
+
| `mime-types` | MIME detection |
|
|
462
|
+
| `sharp` | Image processing |
|
|
463
|
+
| `fluent-ffmpeg` + `@ffmpeg-installer/ffmpeg` | Video/sticker processing |
|
|
597
464
|
|
|
598
|
-
##
|
|
465
|
+
## License
|
|
599
466
|
|
|
600
|
-
|
|
467
|
+
MIT
|