@fazzcode/baileys 2.0.6 → 2.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +1438 -0
- package/lib/Socket/newsletter.js +1 -1
- package/lib/Socket/socket.js +1 -1
- package/lib/Utils/messages-media.js +1 -1
- package/lib/Utils/messages-media.js.bak +601 -0
- package/package.json +3 -2
package/README.MD
ADDED
|
@@ -0,0 +1,1438 @@
|
|
|
1
|
+
# 📱 Baileys - WhatsApp WebSocket API untuk Node.js
|
|
2
|
+
|
|
3
|
+
**Baileys** adalah library Node.js yang powerful untuk mengontrol WhatsApp melalui koneksi WebSocket. Library ini memungkinkan Anda membuat bot WhatsApp, mengotomatisasi pesan, dan mengintegrasikan WhatsApp ke aplikasi Anda tanpa perlu aplikasi official WhatsApp.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 📋 Daftar Isi
|
|
9
|
+
|
|
10
|
+
- [Fitur Utama](#fitur-utama)
|
|
11
|
+
- [Requirement](#requirement)
|
|
12
|
+
- [Instalasi](#instalasi)
|
|
13
|
+
- [Quick Start](#quick-start)
|
|
14
|
+
- [Autentikasi](#autentikasi)
|
|
15
|
+
- [Konfigurasi Koneksi](#konfigurasi-koneksi)
|
|
16
|
+
- [Event & Listener](#event--listener)
|
|
17
|
+
- [Mengirim Pesan](#mengirim-pesan)
|
|
18
|
+
- [Manajemen Grup](#manajemen-grup)
|
|
19
|
+
- [Media & File](#media--file)
|
|
20
|
+
- [Error Handling](#error-handling)
|
|
21
|
+
- [Best Practices](#best-practices)
|
|
22
|
+
- [Contoh Implementasi Lengkap](#contoh-implementasi-lengkap)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## ✨ Fitur Utama
|
|
27
|
+
|
|
28
|
+
- ✅ **Multi-Device Support** - Support untuk multiple devices/session
|
|
29
|
+
- ✅ **Pesan Teks** - Kirim & terima pesan teks
|
|
30
|
+
- ✅ **Media Support** - Image, Video, Audio, Documents, Stickers
|
|
31
|
+
- ✅ **Group Management** - Create, edit, manage groups
|
|
32
|
+
- ✅ **Status & Stories** - Support untuk WhatsApp Status
|
|
33
|
+
- ✅ **Contact & Chat** - Kelola kontak dan chat
|
|
34
|
+
- ✅ **Message React & Quote** - Reply, quote, react to messages
|
|
35
|
+
- ✅ **Typing Indicator** - Show typing status
|
|
36
|
+
- ✅ **User Status** - Get user online/offline status
|
|
37
|
+
- ✅ **Link Preview** - Generate link preview untuk pesan
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 🔧 Requirement
|
|
42
|
+
|
|
43
|
+
- **Node.js** >= 20.0.0 (direkomendasikan LTS terbaru)
|
|
44
|
+
- **npm** atau **yarn**
|
|
45
|
+
- **WhatsApp Account** (hanya 1 account per instance)
|
|
46
|
+
- **System Requirements**:
|
|
47
|
+
- Minimum 512MB RAM
|
|
48
|
+
- Koneksi internet yang stabil
|
|
49
|
+
- Port WebSocket terbuka (biasanya port 443)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 📦 Instalasi
|
|
54
|
+
|
|
55
|
+
### Via NPM
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm install @fazzcode/baileys
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Via Yarn
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
yarn add @fazzcode/baileys
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Dependency Tambahan (Optional)
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Untuk link preview
|
|
71
|
+
npm install link-preview-js
|
|
72
|
+
|
|
73
|
+
# Untuk image processing
|
|
74
|
+
npm install jimp@latest
|
|
75
|
+
|
|
76
|
+
# Untuk audio processing
|
|
77
|
+
npm install audio-decode
|
|
78
|
+
|
|
79
|
+
# Logging
|
|
80
|
+
npm install pino
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## ⚡ Quick Start
|
|
86
|
+
|
|
87
|
+
Berikut adalah contoh paling sederhana untuk memulai bot WhatsApp:
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
import makeWASocket, {
|
|
91
|
+
DisconnectReason,
|
|
92
|
+
useMultiFileAuthState
|
|
93
|
+
} from '@fazzcode/baileys';
|
|
94
|
+
import { Boom } from '@hapi/boom';
|
|
95
|
+
|
|
96
|
+
const startBot = async () => {
|
|
97
|
+
// Setup authentication state
|
|
98
|
+
const { state, saveCreds } = await useMultiFileAuthState('auth_info');
|
|
99
|
+
|
|
100
|
+
const sock = makeWASocket({
|
|
101
|
+
auth: state,
|
|
102
|
+
printQRInTerminal: true
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Save credentials after every connection update
|
|
106
|
+
sock.ev.on('creds.update', saveCreds);
|
|
107
|
+
|
|
108
|
+
// Handle connection events
|
|
109
|
+
sock.ev.on('connection.update', async (update) => {
|
|
110
|
+
const { connection, lastDisconnect, qr } = update;
|
|
111
|
+
|
|
112
|
+
if (qr) {
|
|
113
|
+
console.log('📱 Scan QR code di atas dengan WhatsApp Anda');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (connection === 'open') {
|
|
117
|
+
console.log('✅ Bot terhubung dengan WhatsApp');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (connection === 'close') {
|
|
121
|
+
const shouldReconnect =
|
|
122
|
+
(lastDisconnect?.error)?.output?.statusCode !== DisconnectReason.loggedOut;
|
|
123
|
+
if (shouldReconnect) {
|
|
124
|
+
startBot();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Handle incoming messages
|
|
130
|
+
sock.ev.on('messages.upsert', async (m) => {
|
|
131
|
+
for (const msg of m.messages) {
|
|
132
|
+
if (!msg.message) continue; // Skip empty messages
|
|
133
|
+
|
|
134
|
+
const text = msg.message.conversation ||
|
|
135
|
+
msg.message.extendedTextMessage?.text || '';
|
|
136
|
+
|
|
137
|
+
console.log(`📬 Pesan dari ${msg.key.remoteJid}: ${text}`);
|
|
138
|
+
|
|
139
|
+
// Auto reply
|
|
140
|
+
await sock.sendMessage(msg.key.remoteJid, {
|
|
141
|
+
text: 'Terima kasih pesan Anda! Bot sedang belajar.'
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
startBot().catch(console.error);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 🔐 Autentikasi
|
|
153
|
+
|
|
154
|
+
### 1. Multi-File Auth State (Recommended)
|
|
155
|
+
|
|
156
|
+
Menyimpan kredensial dalam multiple file untuk keamanan lebih baik:
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
import { useMultiFileAuthState } from '@fazzcode/baileys';
|
|
160
|
+
|
|
161
|
+
const { state, saveCreds } = await useMultiFileAuthState('auth_info');
|
|
162
|
+
|
|
163
|
+
// Structure folder:
|
|
164
|
+
// auth_info/
|
|
165
|
+
// ├── creds.json (main credentials)
|
|
166
|
+
// ├── sessions.json
|
|
167
|
+
// ├── pre-keys.json
|
|
168
|
+
// └── app-state-sync-key-*.json
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**File yang Generated:**
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
auth_info/
|
|
175
|
+
├── creds.json # Main credentials & connection info
|
|
176
|
+
├── sessions.json # Device sessions
|
|
177
|
+
├── app-state-sync-key-{id}.json # Session keys untuk sync
|
|
178
|
+
└── pre-keys.json # Pre-encryption keys
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 2. Pairing Code
|
|
182
|
+
|
|
183
|
+
Untuk WhatsApp Business, gunakan pairing code instead of QR:
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
import makeWASocket from '@fazzcode/baileys';
|
|
187
|
+
|
|
188
|
+
const sock = makeWASocket({
|
|
189
|
+
auth: state,
|
|
190
|
+
method: 'phone-number' // Request pairing code
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// **IMPORTANT**: Pairing code harus di-request IMMEDIATELY setelah makeWASocket
|
|
194
|
+
// dengan delay minimal 1.5 detik untuk WhatsApp Business compatibility
|
|
195
|
+
sock.ev.on('connection.update', async (update) => {
|
|
196
|
+
if (update.receivedPairingCode) {
|
|
197
|
+
console.log('🔐 Pairing Code:', update.pairingCode);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 3. Save & Load Credentials
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
// Load existing credentials
|
|
206
|
+
const loadCreds = async () => {
|
|
207
|
+
try {
|
|
208
|
+
const creds = JSON.parse(
|
|
209
|
+
fs.readFileSync('auth_info/creds.json', 'utf-8')
|
|
210
|
+
);
|
|
211
|
+
return creds;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.log('Credentials not found, starting fresh');
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// Save credentials after update
|
|
219
|
+
sock.ev.on('creds.update', () => {
|
|
220
|
+
saveCreds();
|
|
221
|
+
console.log('✅ Credentials saved');
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## ⚙️ Konfigurasi Koneksi
|
|
228
|
+
|
|
229
|
+
### Default Configuration
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
const sock = makeWASocket({
|
|
233
|
+
// ===== AUTHENTICATION =====
|
|
234
|
+
auth: state,
|
|
235
|
+
printQRInTerminal: true,
|
|
236
|
+
|
|
237
|
+
// ===== BROWSER SETTINGS =====
|
|
238
|
+
browser: ['Ubuntu', 'Chrome', '20.0.04'], // ⭐ Important untuk stability
|
|
239
|
+
|
|
240
|
+
// ===== CONNECTION SETTINGS =====
|
|
241
|
+
connectTimeoutMs: 20000, // Timeout untuk koneksi (ms)
|
|
242
|
+
keepAliveIntervalMs: 30000, // Keep-alive interval (ms)
|
|
243
|
+
waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat',
|
|
244
|
+
|
|
245
|
+
// ===== MESSAGE SETTINGS =====
|
|
246
|
+
markOnlineOnConnect: true, // Mark as online when connected
|
|
247
|
+
emitOwnEvents: true, // Emit own messages as events
|
|
248
|
+
maxMsgRetryCount: 5, // Max retry untuk gagal kirim
|
|
249
|
+
|
|
250
|
+
// ===== SYNC SETTINGS =====
|
|
251
|
+
syncFullHistory: true, // Sync full chat history
|
|
252
|
+
shouldSyncHistoryMessage: () => true,
|
|
253
|
+
|
|
254
|
+
// ===== QUERY & TIMEOUT =====
|
|
255
|
+
defaultQueryTimeoutMs: 60000, // Default query timeout
|
|
256
|
+
fireInitQueries: true, // Fire initial queries
|
|
257
|
+
|
|
258
|
+
// ===== OTHER =====
|
|
259
|
+
countryCode: 'US', // Country code
|
|
260
|
+
linkPreviewImageThumbnailWidth: 192, // Link preview width
|
|
261
|
+
enableAutoSessionRecreation: true,
|
|
262
|
+
enableRecentMessageCache: true
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Custom Configuration Example
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
import { Browsers } from '@fazzcode/baileys';
|
|
270
|
+
|
|
271
|
+
const customConfig = {
|
|
272
|
+
auth: state,
|
|
273
|
+
printQRInTerminal: false, // Disable QR di terminal
|
|
274
|
+
|
|
275
|
+
// Use different browser profile untuk bypass detection
|
|
276
|
+
browser: Browsers.macOS('Safari'), // Safari on macOS
|
|
277
|
+
// atau
|
|
278
|
+
// browser: Browsers.windows('Chrome'), // Chrome on Windows
|
|
279
|
+
// browser: Browsers.iOS('Safari'), // iPhone Safari
|
|
280
|
+
|
|
281
|
+
// Custom logger
|
|
282
|
+
logger: customLogger.child({ module: 'whatsapp' }),
|
|
283
|
+
|
|
284
|
+
// Message patches
|
|
285
|
+
patchMessageBeforeSending: (msg) => {
|
|
286
|
+
// Modify message sebelum dikirim
|
|
287
|
+
msg.timestamp = Date.now();
|
|
288
|
+
return msg;
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
// Ignore certain JIDs
|
|
292
|
+
shouldIgnoreJid: (jid) => {
|
|
293
|
+
return jid.includes('status@broadcast');
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
// Custom message retrieval
|
|
297
|
+
getMessage: async (key) => {
|
|
298
|
+
// Fetch message dari database
|
|
299
|
+
return await db.getMessage(key);
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
// Custom group metadata
|
|
303
|
+
cachedGroupMetadata: async (jid) => {
|
|
304
|
+
return await db.getGroupInfo(jid);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const sock = makeWASocket(customConfig);
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## 📡 Event & Listener
|
|
314
|
+
|
|
315
|
+
### Connection Events
|
|
316
|
+
|
|
317
|
+
```javascript
|
|
318
|
+
sock.ev.on('connection.update', (update) => {
|
|
319
|
+
const {
|
|
320
|
+
connection, // Connection status
|
|
321
|
+
lastDisconnect, // Last disconnect info
|
|
322
|
+
isNewLogin, // Is new login?
|
|
323
|
+
qr, // QR code
|
|
324
|
+
receivedPairingCode // Received pairing code?
|
|
325
|
+
} = update;
|
|
326
|
+
|
|
327
|
+
console.log('Connection Update:', {
|
|
328
|
+
connection,
|
|
329
|
+
lastDisconnect,
|
|
330
|
+
isNewLogin
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
if (connection === 'open') {
|
|
334
|
+
console.log('✅ Connected');
|
|
335
|
+
} else if (connection === 'close') {
|
|
336
|
+
console.log('❌ Disconnected');
|
|
337
|
+
|
|
338
|
+
// Check disconnect reason
|
|
339
|
+
const reason = lastDisconnect?.error?.output?.statusCode;
|
|
340
|
+
if (reason === DisconnectReason.loggedOut) {
|
|
341
|
+
console.log('👤 Logout by user');
|
|
342
|
+
} else if (reason === DisconnectReason.connectionClosed) {
|
|
343
|
+
console.log('🔌 Connection closed');
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Message Events
|
|
350
|
+
|
|
351
|
+
```javascript
|
|
352
|
+
// Incoming & Outgoing Messages
|
|
353
|
+
sock.ev.on('messages.upsert', async ({ messages, type }) => {
|
|
354
|
+
for (const msg of messages) {
|
|
355
|
+
const {
|
|
356
|
+
key, // Message key (remoteJid, fromMe, id, etc)
|
|
357
|
+
message, // Message content
|
|
358
|
+
messageTimestamp,
|
|
359
|
+
status, // Message status (pending, sent, delivered, read)
|
|
360
|
+
participant // For groups: who sent it
|
|
361
|
+
} = msg;
|
|
362
|
+
|
|
363
|
+
// Message type
|
|
364
|
+
if (type === 'notify') {
|
|
365
|
+
console.log('📬 New message received');
|
|
366
|
+
} else if (type === 'append') {
|
|
367
|
+
console.log('📝 Message appended');
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Message reactions
|
|
373
|
+
sock.ev.on('messages.reaction', async (reactions) => {
|
|
374
|
+
for (const reaction of reactions) {
|
|
375
|
+
console.log('👍 Reaction:', reaction);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Chat Events
|
|
381
|
+
|
|
382
|
+
```javascript
|
|
383
|
+
sock.ev.on('chats.set', (chats) => {
|
|
384
|
+
console.log(`📚 Loaded ${chats.length} chats`);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
sock.ev.on('chats.update', (chats) => {
|
|
388
|
+
for (const chat of chats) {
|
|
389
|
+
console.log(`Chat updated: ${chat.name}`);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
sock.ev.on('chats.delete', (keys) => {
|
|
394
|
+
console.log(`🗑️ ${keys.length} chats deleted`);
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Group Events
|
|
399
|
+
|
|
400
|
+
```javascript
|
|
401
|
+
sock.ev.on('groups.update', (updates) => {
|
|
402
|
+
for (const update of updates) {
|
|
403
|
+
console.log('👥 Group updated:', update);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
sock.ev.on('group-participants.update', (update) => {
|
|
408
|
+
const { id, participants, action } = update;
|
|
409
|
+
console.log(`Participant ${action} in ${id}:`, participants);
|
|
410
|
+
});
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Contact Events
|
|
414
|
+
|
|
415
|
+
```javascript
|
|
416
|
+
sock.ev.on('contacts.set', (contacts) => {
|
|
417
|
+
console.log(`📇 Loaded ${contacts.length} contacts`);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
sock.ev.on('contacts.upsert', (contacts) => {
|
|
421
|
+
console.log('Contacts updated:', contacts);
|
|
422
|
+
});
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Status & Presence
|
|
426
|
+
|
|
427
|
+
```javascript
|
|
428
|
+
sock.ev.on('presence.update', (presences) => {
|
|
429
|
+
for (const [jid, presence] of Object.entries(presences)) {
|
|
430
|
+
// last_seen: timestamp kapan last online
|
|
431
|
+
// online: true/false
|
|
432
|
+
console.log(`${jid}: ${presence.lastSeen ? 'Online' : 'Offline'}`);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Credentials Update
|
|
438
|
+
|
|
439
|
+
```javascript
|
|
440
|
+
sock.ev.on('creds.update', () => {
|
|
441
|
+
saveCreds();
|
|
442
|
+
console.log('🔐 Credentials updated');
|
|
443
|
+
});
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## 💬 Mengirim Pesan
|
|
449
|
+
|
|
450
|
+
### 1. Pesan Teks
|
|
451
|
+
|
|
452
|
+
```javascript
|
|
453
|
+
// Simple text message
|
|
454
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
455
|
+
text: 'Halo, ini pesan dari bot!'
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Extended text message (dengan formatting)
|
|
459
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
460
|
+
text: 'Pesan dengan *bold* dan _italic_',
|
|
461
|
+
// WhatsApp support formatting terbatas
|
|
462
|
+
});
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### 2. Format JID (Jabber ID)
|
|
466
|
+
|
|
467
|
+
```javascript
|
|
468
|
+
// Personal chat
|
|
469
|
+
const jid1 = '6281234567890@s.whatsapp.net';
|
|
470
|
+
|
|
471
|
+
// Group chat
|
|
472
|
+
const jid2 = '120363023179xxxxxx-1234567890@g.us';
|
|
473
|
+
|
|
474
|
+
// Status/Broadcast
|
|
475
|
+
const jid3 = 'status@broadcast';
|
|
476
|
+
|
|
477
|
+
// Function untuk convert no telepon ke JID
|
|
478
|
+
const phoneToJid = (phone) => {
|
|
479
|
+
let cleaned = phone.replace(/\D/g, '');
|
|
480
|
+
if (!cleaned.startsWith('62')) {
|
|
481
|
+
cleaned = '62' + cleaned.substring(1);
|
|
482
|
+
}
|
|
483
|
+
return `${cleaned}@s.whatsapp.net`;
|
|
484
|
+
};
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### 3. Pesan dengan Mention
|
|
488
|
+
|
|
489
|
+
```javascript
|
|
490
|
+
await sock.sendMessage('120363023179xxxxxx-1234567890@g.us', {
|
|
491
|
+
text: 'Halo @6281234567890 dan @6289876543210',
|
|
492
|
+
mentions: ['6281234567890@s.whatsapp.net', '6289876543210@s.whatsapp.net']
|
|
493
|
+
});
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### 4. Pesan dengan Quote/Reply
|
|
497
|
+
|
|
498
|
+
```javascript
|
|
499
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
500
|
+
text: 'Ini adalah reply',
|
|
501
|
+
quoted: msg // msg dari messages.upsert event
|
|
502
|
+
});
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### 5. Pesan dengan Reaction/Emoji
|
|
506
|
+
|
|
507
|
+
```javascript
|
|
508
|
+
await sock.sendMessage(msg.key.remoteJid, {
|
|
509
|
+
react: {
|
|
510
|
+
text: '😂', // Emoji reaction
|
|
511
|
+
key: msg.key
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
### 6. Pesan Template/Button
|
|
517
|
+
|
|
518
|
+
```javascript
|
|
519
|
+
// Template message dengan buttons
|
|
520
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
521
|
+
templateButtons: [
|
|
522
|
+
{
|
|
523
|
+
index: 1,
|
|
524
|
+
urlButton: {
|
|
525
|
+
displayText: 'Visit Website',
|
|
526
|
+
url: 'https://example.com'
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
index: 2,
|
|
531
|
+
callButton: {
|
|
532
|
+
displayText: 'Call Me',
|
|
533
|
+
phoneNumber: '+6281234567890'
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
index: 3,
|
|
538
|
+
quickReplyButton: {
|
|
539
|
+
displayText: 'Reply Quick',
|
|
540
|
+
id: 'quick_reply_1'
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
],
|
|
544
|
+
body: 'Pilih salah satu opsi di bawah'
|
|
545
|
+
});
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### 7. Pesan dengan Link Preview
|
|
549
|
+
|
|
550
|
+
```javascript
|
|
551
|
+
// Kirim link dengan preview
|
|
552
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
553
|
+
text: 'Check this out: https://github.com/WhiskeySockets/Baileys',
|
|
554
|
+
linkPreview: true // Generate preview otomatis
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// Atau custom link preview
|
|
558
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
559
|
+
text: 'https://example.com',
|
|
560
|
+
linkPreview: {
|
|
561
|
+
title: 'Example Website',
|
|
562
|
+
description: 'This is an example',
|
|
563
|
+
jpegThumbnail: Buffer.from('...') // Image buffer
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### 8. Typing Indicator
|
|
569
|
+
|
|
570
|
+
```javascript
|
|
571
|
+
// Show typing status
|
|
572
|
+
await sock.presenceSubscribe('6281234567890@s.whatsapp.net');
|
|
573
|
+
await sock.sendPresenceUpdate('typing', '6281234567890@s.whatsapp.net');
|
|
574
|
+
|
|
575
|
+
// After finishing, send 'available'
|
|
576
|
+
setTimeout(() => {
|
|
577
|
+
sock.sendPresenceUpdate('available', '6281234567890@s.whatsapp.net');
|
|
578
|
+
}, 3000);
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
## 🖼️ Media & File
|
|
584
|
+
|
|
585
|
+
### 1. Mengirim Gambar
|
|
586
|
+
|
|
587
|
+
```javascript
|
|
588
|
+
import fs from 'fs';
|
|
589
|
+
|
|
590
|
+
// From local file
|
|
591
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
592
|
+
image: fs.readFileSync('path/to/image.jpg'),
|
|
593
|
+
caption: 'Ini adalah caption untuk gambar'
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// From URL
|
|
597
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
598
|
+
image: { url: 'https://example.com/image.jpg' },
|
|
599
|
+
caption: 'Gambar dari URL'
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// From Buffer
|
|
603
|
+
const imageBuffer = Buffer.from(imageData);
|
|
604
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
605
|
+
image: imageBuffer,
|
|
606
|
+
caption: 'Gambar dari buffer'
|
|
607
|
+
});
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### 2. Mengirim Video
|
|
611
|
+
|
|
612
|
+
```javascript
|
|
613
|
+
// Local video
|
|
614
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
615
|
+
video: fs.readFileSync('path/to/video.mp4'),
|
|
616
|
+
caption: 'Ini adalah video',
|
|
617
|
+
gifPlayback: false // Set true untuk video loop
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
// Video dari URL
|
|
621
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
622
|
+
video: { url: 'https://example.com/video.mp4' },
|
|
623
|
+
caption: 'Video dari URL'
|
|
624
|
+
});
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
### 3. Mengirim Audio
|
|
628
|
+
|
|
629
|
+
```javascript
|
|
630
|
+
// Audio/Musik
|
|
631
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
632
|
+
audio: fs.readFileSync('path/to/audio.mp3'),
|
|
633
|
+
mimetype: 'audio/mpeg',
|
|
634
|
+
ptt: false // Set true untuk voice note
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
// Voice Note (PTT)
|
|
638
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
639
|
+
audio: fs.readFileSync('path/to/voice.ogg'),
|
|
640
|
+
mimetype: 'audio/ogg; codecs=opus',
|
|
641
|
+
ptt: true // Mark sebagai voice note
|
|
642
|
+
});
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### 4. Mengirim Document/File
|
|
646
|
+
|
|
647
|
+
```javascript
|
|
648
|
+
// Document
|
|
649
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
650
|
+
document: fs.readFileSync('path/to/document.pdf'),
|
|
651
|
+
mimetype: 'application/pdf',
|
|
652
|
+
fileName: 'My Document.pdf'
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
// Text file
|
|
656
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
657
|
+
document: Buffer.from('File content here'),
|
|
658
|
+
mimetype: 'text/plain',
|
|
659
|
+
fileName: 'text.txt'
|
|
660
|
+
});
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### 5. Mengirim Sticker
|
|
664
|
+
|
|
665
|
+
```javascript
|
|
666
|
+
// Sticker dari file
|
|
667
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
668
|
+
sticker: fs.readFileSync('path/to/sticker.webp'),
|
|
669
|
+
pack: 'My Bot', // Sticker pack name
|
|
670
|
+
author: 'FazzCode', // Sticker author
|
|
671
|
+
categories: ['😀', '👍'] // Sticker categories
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
// Sticker dari URL
|
|
675
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
676
|
+
sticker: { url: 'https://example.com/sticker.webp' },
|
|
677
|
+
pack: 'My Bot',
|
|
678
|
+
author: 'FazzCode'
|
|
679
|
+
});
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### 6. Mengirim Kontak
|
|
683
|
+
|
|
684
|
+
```javascript
|
|
685
|
+
// Send contact
|
|
686
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
687
|
+
contacts: {
|
|
688
|
+
displayName: 'My Contact',
|
|
689
|
+
contacts: [
|
|
690
|
+
{
|
|
691
|
+
vcard: `BEGIN:VCARD\nVERSION:3.0\nFN:John Doe\nTEL;type=CELL;type=VOICE;waid=6281234567890:+62 812 3456 7890\nEND:VCARD`
|
|
692
|
+
}
|
|
693
|
+
]
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
### 7. Mengirim Location
|
|
699
|
+
|
|
700
|
+
```javascript
|
|
701
|
+
// Send location
|
|
702
|
+
await sock.sendMessage('6281234567890@s.whatsapp.net', {
|
|
703
|
+
location: {
|
|
704
|
+
degreesLatitude: -6.2088,
|
|
705
|
+
degreesLongitude: 106.8456,
|
|
706
|
+
name: 'Jakarta, Indonesia',
|
|
707
|
+
address: 'Jalan Sudirman'
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### 8. Mengunduh Media dari Pesan
|
|
713
|
+
|
|
714
|
+
```javascript
|
|
715
|
+
import { downloadMediaMessage } from '@fazzcode/baileys';
|
|
716
|
+
|
|
717
|
+
sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
718
|
+
for (const msg of messages) {
|
|
719
|
+
const messageType = Object.keys(msg.message || {})[0];
|
|
720
|
+
|
|
721
|
+
if (messageType === 'imageMessage' ||
|
|
722
|
+
messageType === 'videoMessage' ||
|
|
723
|
+
messageType === 'audioMessage' ||
|
|
724
|
+
messageType === 'documentMessage') {
|
|
725
|
+
|
|
726
|
+
try {
|
|
727
|
+
const buffer = await downloadMediaMessage(
|
|
728
|
+
msg,
|
|
729
|
+
'buffer', // Download type: buffer, stream, filename
|
|
730
|
+
{},
|
|
731
|
+
{
|
|
732
|
+
logger,
|
|
733
|
+
reuploadRequest: sock.updateMediaMessage
|
|
734
|
+
}
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
// Save to file
|
|
738
|
+
fs.writeFileSync(`downloaded_media`, buffer);
|
|
739
|
+
console.log('✅ Media downloaded');
|
|
740
|
+
} catch (error) {
|
|
741
|
+
console.error('❌ Download failed:', error);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
---
|
|
749
|
+
|
|
750
|
+
## 👥 Manajemen Grup
|
|
751
|
+
|
|
752
|
+
### 1. Create Group
|
|
753
|
+
|
|
754
|
+
```javascript
|
|
755
|
+
// Create new group
|
|
756
|
+
const groupInfo = await sock.groupCreate(
|
|
757
|
+
'Group Name', // Group name
|
|
758
|
+
[
|
|
759
|
+
'6281234567890@s.whatsapp.net',
|
|
760
|
+
'6289876543210@s.whatsapp.net'
|
|
761
|
+
], // Participants
|
|
762
|
+
'Group description' // Description (optional)
|
|
763
|
+
);
|
|
764
|
+
|
|
765
|
+
console.log('✅ Group created:', groupInfo.gid);
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
### 2. Join/Leave Group
|
|
769
|
+
|
|
770
|
+
```javascript
|
|
771
|
+
// Leave group
|
|
772
|
+
await sock.groupLeave('120363023179xxxxxx-1234567890@g.us');
|
|
773
|
+
|
|
774
|
+
// Accept group invite (automatic on response)
|
|
775
|
+
// Note: Untuk join melalui link, gunakan invite code
|
|
776
|
+
await sock.groupAcceptInvite('invite-code');
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### 3. Kelola Anggota
|
|
780
|
+
|
|
781
|
+
```javascript
|
|
782
|
+
// Add members
|
|
783
|
+
await sock.groupParticipantsUpdate(
|
|
784
|
+
'120363023179xxxxxx-1234567890@g.us',
|
|
785
|
+
['6281234567890@s.whatsapp.net'],
|
|
786
|
+
'add'
|
|
787
|
+
);
|
|
788
|
+
|
|
789
|
+
// Remove members
|
|
790
|
+
await sock.groupParticipantsUpdate(
|
|
791
|
+
'120363023179xxxxxx-1234567890@g.us',
|
|
792
|
+
['6281234567890@s.whatsapp.net'],
|
|
793
|
+
'remove'
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
// Promote to admin
|
|
797
|
+
await sock.groupParticipantsUpdate(
|
|
798
|
+
'120363023179xxxxxx-1234567890@g.us',
|
|
799
|
+
['6281234567890@s.whatsapp.net'],
|
|
800
|
+
'promote'
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
// Demote admin
|
|
804
|
+
await sock.groupParticipantsUpdate(
|
|
805
|
+
'120363023179xxxxxx-1234567890@g.us',
|
|
806
|
+
['6281234567890@s.whatsapp.net'],
|
|
807
|
+
'demote'
|
|
808
|
+
);
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
### 4. Update Group Info
|
|
812
|
+
|
|
813
|
+
```javascript
|
|
814
|
+
// Update group subject (name)
|
|
815
|
+
await sock.groupUpdateSubject(
|
|
816
|
+
'120363023179xxxxxx-1234567890@g.us',
|
|
817
|
+
'New Group Name'
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
// Update group description
|
|
821
|
+
await sock.groupUpdateDescription(
|
|
822
|
+
'120363023179xxxxxx-1234567890@g.us',
|
|
823
|
+
'New group description'
|
|
824
|
+
);
|
|
825
|
+
|
|
826
|
+
// Update group icon
|
|
827
|
+
await sock.updateGroupPicture(
|
|
828
|
+
'120363023179xxxxxx-1234567890@g.us',
|
|
829
|
+
fs.readFileSync('path/to/image.jpg')
|
|
830
|
+
);
|
|
831
|
+
|
|
832
|
+
// Update group settings
|
|
833
|
+
await sock.groupSettingUpdate(
|
|
834
|
+
'120363023179xxxxxx-1234567890@g.us',
|
|
835
|
+
'announcement' // Only admins can send messages
|
|
836
|
+
);
|
|
837
|
+
|
|
838
|
+
// Reset invite link
|
|
839
|
+
await sock.groupInviteCode('120363023179xxxxxx-1234567890@g.us');
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
### 5. Get Group Info
|
|
843
|
+
|
|
844
|
+
```javascript
|
|
845
|
+
// Get group metadata
|
|
846
|
+
const metadata = await sock.groupMetadata(
|
|
847
|
+
'120363023179xxxxxx-1234567890@g.us'
|
|
848
|
+
);
|
|
849
|
+
|
|
850
|
+
console.log({
|
|
851
|
+
name: metadata.subject,
|
|
852
|
+
description: metadata.desc,
|
|
853
|
+
participants: metadata.participants.length,
|
|
854
|
+
admins: metadata.participants.filter(p => p.admin).map(p => p.id),
|
|
855
|
+
owner: metadata.owner,
|
|
856
|
+
created: new Date(metadata.creation * 1000)
|
|
857
|
+
});
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
### 6. Kelola Group Admin
|
|
861
|
+
|
|
862
|
+
```javascript
|
|
863
|
+
// Grant admin role
|
|
864
|
+
await sock.groupParticipantsUpdate(
|
|
865
|
+
'120363023179xxxxxx-1234567890@g.us',
|
|
866
|
+
['6281234567890@s.whatsapp.net'],
|
|
867
|
+
'promote'
|
|
868
|
+
);
|
|
869
|
+
|
|
870
|
+
// Remove admin role
|
|
871
|
+
await sock.groupParticipantsUpdate(
|
|
872
|
+
'120363023179xxxxxx-1234567890@g.us',
|
|
873
|
+
['6281234567890@s.whatsapp.net'],
|
|
874
|
+
'demote'
|
|
875
|
+
);
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
---
|
|
879
|
+
|
|
880
|
+
## ⚠️ Error Handling
|
|
881
|
+
|
|
882
|
+
### Basic Error Handling
|
|
883
|
+
|
|
884
|
+
```javascript
|
|
885
|
+
import Boom from '@hapi/boom';
|
|
886
|
+
|
|
887
|
+
sock.ev.on('connection.update', (update) => {
|
|
888
|
+
const { connection, lastDisconnect } = update;
|
|
889
|
+
|
|
890
|
+
if (connection === 'close') {
|
|
891
|
+
const error = lastDisconnect?.error;
|
|
892
|
+
|
|
893
|
+
if (error instanceof Boom) {
|
|
894
|
+
const statusCode = error.output.statusCode;
|
|
895
|
+
|
|
896
|
+
switch (statusCode) {
|
|
897
|
+
case 401: // Unauthorized
|
|
898
|
+
console.log('❌ Login failed');
|
|
899
|
+
break;
|
|
900
|
+
case 408: // Connection lost
|
|
901
|
+
console.log('🔌 Connection lost, reconnecting...');
|
|
902
|
+
startBot();
|
|
903
|
+
break;
|
|
904
|
+
case 411: // Multi-device mismatch
|
|
905
|
+
console.log('⚠️ Multi-device mismatch');
|
|
906
|
+
break;
|
|
907
|
+
case 503: // Service unavailable
|
|
908
|
+
console.log('🔧 WhatsApp service unavailable');
|
|
909
|
+
break;
|
|
910
|
+
default:
|
|
911
|
+
console.log(`Unknown error: ${statusCode}`);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
### Message Sending Error
|
|
919
|
+
|
|
920
|
+
```javascript
|
|
921
|
+
const sendMessageSafe = async (jid, content) => {
|
|
922
|
+
try {
|
|
923
|
+
const result = await sock.sendMessage(jid, content);
|
|
924
|
+
console.log('✅ Message sent');
|
|
925
|
+
return result;
|
|
926
|
+
} catch (error) {
|
|
927
|
+
console.error('❌ Send failed:', error.message);
|
|
928
|
+
|
|
929
|
+
if (error.message.includes('401')) {
|
|
930
|
+
console.log('Re-login required');
|
|
931
|
+
} else if (error.message.includes('rate limited')) {
|
|
932
|
+
console.log('⏱️ Rate limited, retrying later...');
|
|
933
|
+
// Implement exponential backoff
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return null;
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
### Retry Logic dengan Exponential Backoff
|
|
942
|
+
|
|
943
|
+
```javascript
|
|
944
|
+
const sendMessageWithRetry = async (jid, content, maxRetries = 3) => {
|
|
945
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
946
|
+
try {
|
|
947
|
+
return await sock.sendMessage(jid, content);
|
|
948
|
+
} catch (error) {
|
|
949
|
+
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
|
|
950
|
+
|
|
951
|
+
if (i < maxRetries - 1) {
|
|
952
|
+
console.log(`Retry ${i + 1}/${maxRetries} after ${delay}ms`);
|
|
953
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
954
|
+
} else {
|
|
955
|
+
throw error;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
---
|
|
963
|
+
|
|
964
|
+
## 🎯 Best Practices
|
|
965
|
+
|
|
966
|
+
### 1. Authentication & Pairing Code
|
|
967
|
+
|
|
968
|
+
```javascript
|
|
969
|
+
// ⭐ IMPORTANT: Request pairing code immediately dengan delay 1.5s
|
|
970
|
+
setTimeout(async () => {
|
|
971
|
+
try {
|
|
972
|
+
const pairingCode = await sock.requestPairingCode('6281234567890');
|
|
973
|
+
console.log('Pairing Code:', pairingCode);
|
|
974
|
+
} catch (error) {
|
|
975
|
+
console.error('Failed to get pairing code:', error);
|
|
976
|
+
}
|
|
977
|
+
}, 1500);
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
### 2. Browser Configuration untuk Stability
|
|
981
|
+
|
|
982
|
+
```javascript
|
|
983
|
+
// Use correct browser settings untuk avoid detection/blocking
|
|
984
|
+
const sock = makeWASocket({
|
|
985
|
+
// ✅ RECOMMENDED FOR STABILITY
|
|
986
|
+
browser: ['Ubuntu', 'Chrome', '20.0.04'],
|
|
987
|
+
|
|
988
|
+
// Alternative options
|
|
989
|
+
// browser: Browsers.macOS('Safari'),
|
|
990
|
+
// browser: Browsers.windows('Edge'),
|
|
991
|
+
// browser: Browsers.iOS('Safari'), // iPhone
|
|
992
|
+
|
|
993
|
+
// Other important settings
|
|
994
|
+
connectTimeoutMs: 20000,
|
|
995
|
+
keepAliveIntervalMs: 30000,
|
|
996
|
+
enableAutoSessionRecreation: true
|
|
997
|
+
});
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
### 3. Prevent WhatsApp Error 428 (Connection Replaced)
|
|
1001
|
+
|
|
1002
|
+
```javascript
|
|
1003
|
+
// Use exponential backoff untuk reconnect
|
|
1004
|
+
let reconnectAttempts = 0;
|
|
1005
|
+
const maxReconnectAttempts = 5;
|
|
1006
|
+
|
|
1007
|
+
const handleDisconnect = async () => {
|
|
1008
|
+
reconnectAttempts++;
|
|
1009
|
+
|
|
1010
|
+
if (reconnectAttempts > maxReconnectAttempts) {
|
|
1011
|
+
console.error('❌ Max reconnect attempts reached');
|
|
1012
|
+
process.exit(1);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
const delay = Math.pow(2, reconnectAttempts) * 1000;
|
|
1016
|
+
console.log(`Reconnecting after ${delay}ms (attempt ${reconnectAttempts})`);
|
|
1017
|
+
|
|
1018
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
1019
|
+
startBot();
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
|
|
1023
|
+
if (connection === 'close') {
|
|
1024
|
+
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
|
1025
|
+
|
|
1026
|
+
if (statusCode === 428) { // Connection replaced
|
|
1027
|
+
console.log('⚠️ Connection replaced, initiating reconnect...');
|
|
1028
|
+
handleDisconnect();
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
### 4. Graceful Shutdown
|
|
1035
|
+
|
|
1036
|
+
```javascript
|
|
1037
|
+
const gracefulShutdown = async () => {
|
|
1038
|
+
console.log('\n📴 Shutting down...');
|
|
1039
|
+
|
|
1040
|
+
try {
|
|
1041
|
+
// Close connection gracefully
|
|
1042
|
+
await sock.end();
|
|
1043
|
+
console.log('✅ Connection closed');
|
|
1044
|
+
} catch (error) {
|
|
1045
|
+
console.error('Error during shutdown:', error);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
process.exit(0);
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
process.on('SIGINT', gracefulShutdown);
|
|
1052
|
+
process.on('SIGTERM', gracefulShutdown);
|
|
1053
|
+
```
|
|
1054
|
+
|
|
1055
|
+
### 5. Message Processing
|
|
1056
|
+
|
|
1057
|
+
```javascript
|
|
1058
|
+
// Filter empty/invalid messages
|
|
1059
|
+
const isValidMessage = (msg) => {
|
|
1060
|
+
return msg?.message && msg?.key?.remoteJid;
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
// Extract message content safely
|
|
1064
|
+
const getMessageContent = (msg) => {
|
|
1065
|
+
const messageTypes = {
|
|
1066
|
+
conversation: msg.message?.conversation,
|
|
1067
|
+
extendedText: msg.message?.extendedTextMessage?.text,
|
|
1068
|
+
imageCaption: msg.message?.imageMessage?.caption,
|
|
1069
|
+
videoCaption: msg.message?.videoMessage?.caption
|
|
1070
|
+
};
|
|
1071
|
+
|
|
1072
|
+
return Object.values(messageTypes).find(content => content?.trim());
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
// Check if message from self
|
|
1076
|
+
const isFromMe = (msg) => msg.key.fromMe;
|
|
1077
|
+
|
|
1078
|
+
// Check if group message
|
|
1079
|
+
const isGroupMessage = (msg) => msg.key.remoteJid.endsWith('@g.us');
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
### 6. Rate Limiting
|
|
1083
|
+
|
|
1084
|
+
```javascript
|
|
1085
|
+
import PQueue from 'p-queue';
|
|
1086
|
+
|
|
1087
|
+
const messageQueue = new PQueue({
|
|
1088
|
+
concurrency: 1, // Send 1 message at a time
|
|
1089
|
+
interval: 1000, // Per 1 second
|
|
1090
|
+
carryoverConcurrencyCount: false
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
// Send message dengan queue
|
|
1094
|
+
const sendMessageQueued = async (jid, content) => {
|
|
1095
|
+
return messageQueue.add(() =>
|
|
1096
|
+
sock.sendMessage(jid, content)
|
|
1097
|
+
);
|
|
1098
|
+
};
|
|
1099
|
+
```
|
|
1100
|
+
|
|
1101
|
+
### 7. Database Integration (Contoh: SQLite)
|
|
1102
|
+
|
|
1103
|
+
```javascript
|
|
1104
|
+
import Database from 'better-sqlite3';
|
|
1105
|
+
|
|
1106
|
+
const db = new Database('bot.db');
|
|
1107
|
+
|
|
1108
|
+
// Create tables
|
|
1109
|
+
db.exec(`
|
|
1110
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
1111
|
+
id TEXT PRIMARY KEY,
|
|
1112
|
+
jid TEXT,
|
|
1113
|
+
text TEXT,
|
|
1114
|
+
timestamp INTEGER,
|
|
1115
|
+
fromMe BOOLEAN
|
|
1116
|
+
);
|
|
1117
|
+
|
|
1118
|
+
CREATE TABLE IF NOT EXISTS groups (
|
|
1119
|
+
jid TEXT PRIMARY KEY,
|
|
1120
|
+
name TEXT,
|
|
1121
|
+
members INTEGER,
|
|
1122
|
+
lastUpdated INTEGER
|
|
1123
|
+
);
|
|
1124
|
+
`);
|
|
1125
|
+
|
|
1126
|
+
// Save message
|
|
1127
|
+
const saveMessage = (msg) => {
|
|
1128
|
+
const stmt = db.prepare(`
|
|
1129
|
+
INSERT INTO messages (id, jid, text, timestamp, fromMe)
|
|
1130
|
+
VALUES (?, ?, ?, ?, ?)
|
|
1131
|
+
`);
|
|
1132
|
+
|
|
1133
|
+
stmt.run(
|
|
1134
|
+
msg.key.id,
|
|
1135
|
+
msg.key.remoteJid,
|
|
1136
|
+
getMessageContent(msg),
|
|
1137
|
+
msg.messageTimestamp * 1000,
|
|
1138
|
+
msg.key.fromMe
|
|
1139
|
+
);
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
// Save group info
|
|
1143
|
+
const saveGroupInfo = (jid, metadata) => {
|
|
1144
|
+
const stmt = db.prepare(`
|
|
1145
|
+
INSERT OR REPLACE INTO groups (jid, name, members, lastUpdated)
|
|
1146
|
+
VALUES (?, ?, ?, ?)
|
|
1147
|
+
`);
|
|
1148
|
+
|
|
1149
|
+
stmt.run(
|
|
1150
|
+
jid,
|
|
1151
|
+
metadata.subject,
|
|
1152
|
+
metadata.participants.length,
|
|
1153
|
+
Date.now()
|
|
1154
|
+
);
|
|
1155
|
+
};
|
|
1156
|
+
```
|
|
1157
|
+
|
|
1158
|
+
---
|
|
1159
|
+
|
|
1160
|
+
## 📝 Contoh Implementasi Lengkap
|
|
1161
|
+
|
|
1162
|
+
### Bot Echo Sederhana
|
|
1163
|
+
|
|
1164
|
+
```javascript
|
|
1165
|
+
import makeWASocket, {
|
|
1166
|
+
DisconnectReason,
|
|
1167
|
+
useMultiFileAuthState,
|
|
1168
|
+
Browsers
|
|
1169
|
+
} from '@fazzcode/baileys';
|
|
1170
|
+
import { Boom } from '@hapi/boom';
|
|
1171
|
+
import fs from 'fs';
|
|
1172
|
+
|
|
1173
|
+
const startBot = async () => {
|
|
1174
|
+
const { state, saveCreds } = await useMultiFileAuthState('auth_info');
|
|
1175
|
+
|
|
1176
|
+
const sock = makeWASocket({
|
|
1177
|
+
auth: state,
|
|
1178
|
+
browser: ['Ubuntu', 'Chrome', '20.0.04'],
|
|
1179
|
+
printQRInTerminal: true,
|
|
1180
|
+
connectTimeoutMs: 20000,
|
|
1181
|
+
keepAliveIntervalMs: 30000,
|
|
1182
|
+
enableAutoSessionRecreation: true
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
// Save credentials
|
|
1186
|
+
sock.ev.on('creds.update', saveCreds);
|
|
1187
|
+
|
|
1188
|
+
// Handle connection
|
|
1189
|
+
sock.ev.on('connection.update', async (update) => {
|
|
1190
|
+
const { connection, qr } = update;
|
|
1191
|
+
|
|
1192
|
+
if (qr) {
|
|
1193
|
+
console.log('📱 Scan QR code untuk login');
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
if (connection === 'open') {
|
|
1197
|
+
console.log('✅ Bot online');
|
|
1198
|
+
}
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
// Handle messages
|
|
1202
|
+
sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
1203
|
+
for (const msg of messages) {
|
|
1204
|
+
// Ignore own messages
|
|
1205
|
+
if (msg.key.fromMe) continue;
|
|
1206
|
+
|
|
1207
|
+
// Ignore empty messages
|
|
1208
|
+
if (!msg.message) continue;
|
|
1209
|
+
|
|
1210
|
+
const from = msg.key.remoteJid;
|
|
1211
|
+
const text = msg.message.conversation ||
|
|
1212
|
+
msg.message.extendedTextMessage?.text || '';
|
|
1213
|
+
|
|
1214
|
+
// Log
|
|
1215
|
+
console.log(`\n📬 [${new Date().toLocaleTimeString()}] ${from}:`);
|
|
1216
|
+
console.log(` ${text}`);
|
|
1217
|
+
|
|
1218
|
+
// Echo reply
|
|
1219
|
+
if (text) {
|
|
1220
|
+
await sock.sendMessage(from, {
|
|
1221
|
+
text: `Echo: ${text}`
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
|
|
1227
|
+
// Handle errors
|
|
1228
|
+
sock.ev.on('connection.update', async (update) => {
|
|
1229
|
+
const { connection, lastDisconnect } = update;
|
|
1230
|
+
|
|
1231
|
+
if (connection === 'close') {
|
|
1232
|
+
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
|
1233
|
+
|
|
1234
|
+
if (statusCode === DisconnectReason.loggedOut) {
|
|
1235
|
+
console.log('👤 Logged out');
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
console.log(`❌ Disconnected (${statusCode}), reconnecting...`);
|
|
1240
|
+
setTimeout(startBot, 5000);
|
|
1241
|
+
}
|
|
1242
|
+
});
|
|
1243
|
+
};
|
|
1244
|
+
|
|
1245
|
+
// Start
|
|
1246
|
+
startBot().catch(console.error);
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
### Bot dengan Command System
|
|
1250
|
+
|
|
1251
|
+
```javascript
|
|
1252
|
+
import makeWASocket, { useMultiFileAuthState } from '@fazzcode/baileys';
|
|
1253
|
+
|
|
1254
|
+
const PREFIX = '!';
|
|
1255
|
+
|
|
1256
|
+
const commands = {
|
|
1257
|
+
hello: async (sock, msg) => {
|
|
1258
|
+
await sock.sendMessage(msg.key.remoteJid, {
|
|
1259
|
+
text: '👋 Halo, ada yang bisa dibantu?'
|
|
1260
|
+
});
|
|
1261
|
+
},
|
|
1262
|
+
|
|
1263
|
+
info: async (sock, msg) => {
|
|
1264
|
+
const text = `
|
|
1265
|
+
📱 Bot Information:
|
|
1266
|
+
- Name: Echo Bot
|
|
1267
|
+
- Version: 1.0.0
|
|
1268
|
+
- Status: Online
|
|
1269
|
+
- Uptime: ${Math.floor(process.uptime())} seconds
|
|
1270
|
+
`;
|
|
1271
|
+
await sock.sendMessage(msg.key.remoteJid, { text });
|
|
1272
|
+
},
|
|
1273
|
+
|
|
1274
|
+
help: async (sock, msg) => {
|
|
1275
|
+
const text = `
|
|
1276
|
+
📚 Available Commands:
|
|
1277
|
+
!hello - Greeting
|
|
1278
|
+
!info - Bot info
|
|
1279
|
+
!help - Show this help
|
|
1280
|
+
!ping - Check latency
|
|
1281
|
+
`;
|
|
1282
|
+
await sock.sendMessage(msg.key.remoteJid, { text });
|
|
1283
|
+
},
|
|
1284
|
+
|
|
1285
|
+
ping: async (sock, msg) => {
|
|
1286
|
+
const start = Date.now();
|
|
1287
|
+
await sock.sendMessage(msg.key.remoteJid, {
|
|
1288
|
+
text: `⏱️ Pong! (${Date.now() - start}ms)`
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
|
|
1293
|
+
const startBot = async () => {
|
|
1294
|
+
const { state, saveCreds } = await useMultiFileAuthState('auth_info');
|
|
1295
|
+
|
|
1296
|
+
const sock = makeWASocket({
|
|
1297
|
+
auth: state,
|
|
1298
|
+
browser: ['Ubuntu', 'Chrome', '20.0.04'],
|
|
1299
|
+
printQRInTerminal: true
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
sock.ev.on('creds.update', saveCreds);
|
|
1303
|
+
|
|
1304
|
+
sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
1305
|
+
for (const msg of messages) {
|
|
1306
|
+
if (msg.key.fromMe || !msg.message) continue;
|
|
1307
|
+
|
|
1308
|
+
const text = msg.message.conversation?.trim() || '';
|
|
1309
|
+
|
|
1310
|
+
if (!text.startsWith(PREFIX)) continue;
|
|
1311
|
+
|
|
1312
|
+
const [command, ...args] = text.slice(PREFIX.length).split(' ');
|
|
1313
|
+
|
|
1314
|
+
if (command in commands) {
|
|
1315
|
+
try {
|
|
1316
|
+
await commands[command](sock, msg);
|
|
1317
|
+
} catch (error) {
|
|
1318
|
+
console.error(`Error executing ${command}:`, error);
|
|
1319
|
+
await sock.sendMessage(msg.key.remoteJid, {
|
|
1320
|
+
text: '❌ Error executing command'
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
sock.ev.on('connection.update', async (update) => {
|
|
1328
|
+
if (update.connection === 'open') {
|
|
1329
|
+
console.log('✅ Bot online');
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
};
|
|
1333
|
+
|
|
1334
|
+
startBot().catch(console.error);
|
|
1335
|
+
```
|
|
1336
|
+
|
|
1337
|
+
### Bot Group Manager
|
|
1338
|
+
|
|
1339
|
+
```javascript
|
|
1340
|
+
const handleGroupMessage = async (sock, msg) => {
|
|
1341
|
+
const groupJid = msg.key.remoteJid;
|
|
1342
|
+
const sender = msg.key.participant || msg.key.remoteJid;
|
|
1343
|
+
const text = msg.message?.conversation || '';
|
|
1344
|
+
|
|
1345
|
+
// Get group metadata
|
|
1346
|
+
const metadata = await sock.groupMetadata(groupJid);
|
|
1347
|
+
const isAdmin = metadata.admins?.includes(sender);
|
|
1348
|
+
|
|
1349
|
+
if (!text.startsWith('!')) return;
|
|
1350
|
+
|
|
1351
|
+
const [command, ...args] = text.slice(1).split(' ');
|
|
1352
|
+
|
|
1353
|
+
switch (command) {
|
|
1354
|
+
case 'add':
|
|
1355
|
+
if (!isAdmin) {
|
|
1356
|
+
await sock.sendMessage(groupJid, {
|
|
1357
|
+
text: '❌ Hanya admin yang bisa menambah member'
|
|
1358
|
+
});
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
const phoneToAdd = args[0];
|
|
1363
|
+
if (!phoneToAdd) {
|
|
1364
|
+
await sock.sendMessage(groupJid, {
|
|
1365
|
+
text: '❌ Format: !add 62812345678'
|
|
1366
|
+
});
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
const jidToAdd = phoneToAdd + '@s.whatsapp.net';
|
|
1371
|
+
await sock.groupParticipantsUpdate(groupJid, [jidToAdd], 'add');
|
|
1372
|
+
await sock.sendMessage(groupJid, {
|
|
1373
|
+
text: `✅ ${phoneToAdd} telah ditambahkan`
|
|
1374
|
+
});
|
|
1375
|
+
break;
|
|
1376
|
+
|
|
1377
|
+
case 'info':
|
|
1378
|
+
const info = `
|
|
1379
|
+
👥 Group Info:
|
|
1380
|
+
- Name: ${metadata.subject}
|
|
1381
|
+
- Members: ${metadata.participants.length}
|
|
1382
|
+
- Created: ${new Date(metadata.creation * 1000).toLocaleDateString()}
|
|
1383
|
+
- Description: ${metadata.desc || 'N/A'}
|
|
1384
|
+
`;
|
|
1385
|
+
await sock.sendMessage(groupJid, { text: info });
|
|
1386
|
+
break;
|
|
1387
|
+
|
|
1388
|
+
case 'members':
|
|
1389
|
+
const memberList = metadata.participants
|
|
1390
|
+
.slice(0, 10)
|
|
1391
|
+
.map((p, i) => `${i + 1}. ${p.id}`)
|
|
1392
|
+
.join('\n');
|
|
1393
|
+
|
|
1394
|
+
await sock.sendMessage(groupJid, {
|
|
1395
|
+
text: `📋 Group Members (${metadata.participants.length}):\n${memberList}`
|
|
1396
|
+
});
|
|
1397
|
+
break;
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
```
|
|
1401
|
+
|
|
1402
|
+
---
|
|
1403
|
+
|
|
1404
|
+
## 📚 Dokumentasi Lengkap
|
|
1405
|
+
|
|
1406
|
+
- **Event Types**: Connection, Message, Group, Chat, Contact, Presence
|
|
1407
|
+
- **Message Types**: Text, Image, Video, Audio, Document, Sticker, Location, Contact
|
|
1408
|
+
- **JID Format**: `number@s.whatsapp.net` (personal), `id@g.us` (group)
|
|
1409
|
+
- **Message Status**: pending, sent, delivered, read, played
|
|
1410
|
+
- **Error Codes**: 401 (Unauthorized), 408 (Lost), 411 (Device Mismatch), 503 (Service Down)
|
|
1411
|
+
|
|
1412
|
+
---
|
|
1413
|
+
|
|
1414
|
+
## 🔗 Contact
|
|
1415
|
+
|
|
1416
|
+
- **Baileys GitHub**: None
|
|
1417
|
+
- **FazzCode Channel**: https://whatsapp.com/channel/0029Vb66VpnA2pLGOYBeuq0S
|
|
1418
|
+
|
|
1419
|
+
---
|
|
1420
|
+
|
|
1421
|
+
## ⚖️ License
|
|
1422
|
+
|
|
1423
|
+
MIT License - FazzCode © 2026
|
|
1424
|
+
|
|
1425
|
+
---
|
|
1426
|
+
|
|
1427
|
+
## ⚠️ Disclaimer
|
|
1428
|
+
|
|
1429
|
+
- Penggunaan Baileys harus sesuai dengan Terms of Service WhatsApp
|
|
1430
|
+
- Jangan gunakan untuk spam atau aktivitas ilegal
|
|
1431
|
+
- Bot bisa di-ban jika melanggar WhatsApp policies
|
|
1432
|
+
- Use responsibly!
|
|
1433
|
+
|
|
1434
|
+
---
|
|
1435
|
+
|
|
1436
|
+
**Made with ❤️ by FazzCode**
|
|
1437
|
+
|
|
1438
|
+
Untuk support dan update terbaru, join channel Whatsapp: [FazzCodeStudio](https://whatsapp.com/channel/0029Vb66VpnA2pLGOYBeuq0S)
|