wuzapi 1.3.1 → 1.5.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/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  A comprehensive TypeScript client library for the [WuzAPI WhatsApp API](https://github.com/asternic/wuzapi). This library provides a simple and intuitive interface to interact with WhatsApp through the WuzAPI service.
4
4
 
5
- ## Features
5
+ ## 🚀 Features
6
6
 
7
- - 🔥 **Full TypeScript Support** - Complete type definitions for all API endpoints
7
+ - 🔥 **Full TypeScript Support** - Complete type definitions for all API endpoints
8
8
  - 🏗️ **Modular Architecture** - Organized by functionality (admin, session, chat, user, group, webhook)
9
9
  - 🚀 **Promise-based** - Modern async/await support
10
10
  - 🛡️ **Error Handling** - Comprehensive error handling with detailed error types
@@ -12,7 +12,7 @@ A comprehensive TypeScript client library for the [WuzAPI WhatsApp API](https://
12
12
  - 🔧 **Easy Configuration** - Simple setup with minimal configuration
13
13
  - 📖 **Well Documented** - Extensive documentation and examples
14
14
 
15
- ## Installation
15
+ ## 📦 Installation
16
16
 
17
17
  ```bash
18
18
  npm install wuzapi
@@ -24,7 +24,9 @@ or
24
24
  yarn add wuzapi
25
25
  ```
26
26
 
27
- ## Quick Start
27
+ ## Quick Start
28
+
29
+ ### Basic Setup
28
30
 
29
31
  ```typescript
30
32
  import WuzapiClient from "wuzapi";
@@ -40,37 +42,225 @@ await client.session.connect({
40
42
  Immediate: false,
41
43
  });
42
44
 
43
- // Send a text message
45
+ // Send a message
44
46
  await client.chat.sendText({
45
47
  Phone: "5491155554444",
46
- Body: "Hello from WuzAPI!",
48
+ Body: "Hello from WuzAPI! 🎉",
47
49
  });
50
+ ```
48
51
 
49
- // Get session status
50
- const status = await client.session.getStatus();
51
- console.log("Connected:", status.Connected);
52
- console.log("Logged In:", status.LoggedIn);
52
+ ### Login Options
53
+
54
+ #### Option 1: QR Code (Traditional)
55
+ ```typescript
56
+ // Get QR code for scanning
57
+ const qr = await client.session.getQRCode();
58
+ console.log("Scan this QR code:", qr.QRCode);
53
59
  ```
54
60
 
55
- ## Configuration
61
+ #### Option 2: Phone Pairing (New!)
62
+ ```typescript
63
+ // Pair using verification code (SMS/Call)
64
+ await client.session.pairPhone("5491155554444", "123456");
65
+ ```
56
66
 
57
- The client requires a configuration object with the following properties:
67
+ ## 🔧 Configuration
58
68
 
59
69
  ```typescript
60
70
  interface WuzapiConfig {
61
- apiUrl: string; // The WuzAPI server URL
62
- token: string; // Your user authentication token
71
+ apiUrl: string; // Your WuzAPI server URL
72
+ token?: string; // Authentication token (can be provided per request)
63
73
  }
74
+
75
+ // Global token approach
76
+ const client = new WuzapiClient({
77
+ apiUrl: "http://localhost:8080",
78
+ token: "your-token",
79
+ });
80
+
81
+ // Flexible token approach
82
+ const client = new WuzapiClient({
83
+ apiUrl: "http://localhost:8080",
84
+ });
85
+
86
+ // Use different tokens for different operations
87
+ await client.chat.sendText(
88
+ { Phone: "123", Body: "Hello" },
89
+ { token: "user-specific-token" }
90
+ );
91
+ ```
92
+
93
+ ## 💬 Essential Chat Operations
94
+
95
+ ```typescript
96
+ // Send text message
97
+ await client.chat.sendText({
98
+ Phone: "5491155554444",
99
+ Body: "Hello World!",
100
+ });
101
+
102
+ // Send image
103
+ await client.chat.sendImage({
104
+ Phone: "5491155554444",
105
+ Image: "data:image/jpeg;base64,/9j/4AAQ...",
106
+ Caption: "Check this out!",
107
+ });
108
+
109
+ // Send interactive buttons
110
+ await client.chat.sendButtons({
111
+ Phone: "5491155554444",
112
+ Body: "Choose an option:",
113
+ Buttons: [
114
+ { ButtonId: "yes", ButtonText: { DisplayText: "Yes" }, Type: 1 },
115
+ { ButtonId: "no", ButtonText: { DisplayText: "No" }, Type: 1 },
116
+ ],
117
+ });
118
+
119
+ // Send list menu
120
+ await client.chat.sendList({
121
+ Phone: "5491155554444",
122
+ Body: "Select from menu:",
123
+ Title: "Options",
124
+ ButtonText: "View Menu",
125
+ Sections: [{
126
+ Title: "Main Options",
127
+ Rows: [
128
+ { Title: "Option 1", Description: "First choice", RowId: "opt1" },
129
+ { Title: "Option 2", Description: "Second choice", RowId: "opt2" },
130
+ ],
131
+ }],
132
+ });
133
+
134
+ // Send poll
135
+ await client.chat.sendPoll({
136
+ Phone: "5491155554444",
137
+ Name: "What's your favorite color?",
138
+ Options: [{ Name: "Red" }, { Name: "Blue" }, { Name: "Green" }],
139
+ SelectableCount: 1,
140
+ });
141
+ ```
142
+
143
+ ## 👥 Group Management
144
+
145
+ ```typescript
146
+ // Create group
147
+ const group = await client.group.create("My Group", [
148
+ "5491155553934",
149
+ "5491155553935",
150
+ ]);
151
+
152
+ // Get group info
153
+ const info = await client.group.getInfo(group.JID);
154
+
155
+ // Add participants
156
+ await client.group.updateParticipants(
157
+ group.JID,
158
+ "add",
159
+ ["5491155553936"]
160
+ );
161
+
162
+ // Set group settings
163
+ await client.group.setName(group.JID, "New Group Name");
164
+ await client.group.setTopic(group.JID, "Welcome message");
165
+ await client.group.setAnnounce(group.JID, true); // Only admins can send
166
+ ```
167
+
168
+ ## 👤 User Operations
169
+
170
+ ```typescript
171
+ // Check if numbers are WhatsApp users
172
+ const check = await client.user.check(["5491155554444"]);
173
+
174
+ // Get user info
175
+ const info = await client.user.getInfo(["5491155554444"]);
176
+
177
+ // Get contacts
178
+ const contacts = await client.user.getContacts();
179
+
180
+ // Send presence status
181
+ await client.user.sendPresence({
182
+ Phone: "5491155554444",
183
+ State: "available",
184
+ });
185
+ ```
186
+
187
+ ## 🔗 Webhook Setup
188
+
189
+ ```typescript
190
+ // Set webhook URL
191
+ await client.webhook.setWebhook("https://your-server.com/webhook");
192
+
193
+ // Get webhook config
194
+ const config = await client.webhook.getWebhook();
195
+
196
+ // Update webhook
197
+ await client.webhook.updateWebhook("https://new-server.com/webhook");
198
+ ```
199
+
200
+ ## 📚 Examples
201
+
202
+ Check out the complete examples in the `examples/` directory:
203
+
204
+ - **[basic-usage.js](examples/basic-usage.js)** - Getting started, connection, basic operations
205
+ - **[advanced-features.js](examples/advanced-features.js)** - Phone pairing, interactive messages, advanced group management
206
+ - **[chatbot-example.js](examples/chatbot-example.js)** - Complete bot with commands and auto-replies
207
+ - **[webhook-events-example.js](examples/webhook-events-example.js)** - Comprehensive webhook event handling
208
+
209
+ ### Run Examples
210
+
211
+ ```bash
212
+ # Basic usage
213
+ node examples/basic-usage.js
214
+
215
+ # Advanced features
216
+ node examples/advanced-features.js
217
+
218
+ # Start chatbot
219
+ node examples/chatbot-example.js
64
220
  ```
65
221
 
66
- ## API Modules
222
+ ## 🤖 Simple Bot Example
223
+
224
+ ```typescript
225
+ import WuzapiClient from "wuzapi";
226
+
227
+ const client = new WuzapiClient({
228
+ apiUrl: "http://localhost:8080",
229
+ token: "your-token",
230
+ });
231
+
232
+ // Connect and wait for messages
233
+ await client.session.connect({ Subscribe: ["Message"] });
234
+ await client.webhook.setWebhook("https://your-server.com/webhook");
235
+
236
+ // In your webhook handler:
237
+ app.post("/webhook", async (req, res) => {
238
+ const { event } = req.body;
239
+
240
+ if (event?.Message?.conversation) {
241
+ const message = event.Message.conversation;
242
+ const from = event.Info.RemoteJid.replace("@s.whatsapp.net", "");
243
+
244
+ if (message.toLowerCase().includes("hello")) {
245
+ await client.chat.sendText({
246
+ Phone: from,
247
+ Body: "Hello! 👋 How can I help you?",
248
+ });
249
+ }
250
+ }
251
+
252
+ res.json({ success: true });
253
+ });
254
+ ```
67
255
 
68
- The client is organized into logical modules:
256
+ ---
69
257
 
70
- ### Session Module
258
+ ## 📖 Complete API Reference
71
259
 
72
- Manage WhatsApp connection and session state.
260
+ <details>
261
+ <summary><strong>📱 Session Module</strong> - Connection and authentication</summary>
73
262
 
263
+ ### Connection
74
264
  ```typescript
75
265
  // Connect to WhatsApp
76
266
  await client.session.connect({
@@ -81,15 +271,30 @@ await client.session.connect({
81
271
  // Get connection status
82
272
  const status = await client.session.getStatus();
83
273
 
84
- // Get QR code for initial setup
85
- const qr = await client.session.getQRCode();
86
-
87
274
  // Disconnect (keeps session)
88
275
  await client.session.disconnect();
89
276
 
90
277
  // Logout (destroys session)
91
278
  await client.session.logout();
279
+ ```
280
+
281
+ ### Authentication
282
+ ```typescript
283
+ // Get QR code for scanning
284
+ const qr = await client.session.getQRCode();
285
+
286
+ // Pair phone using verification code (alternative to QR)
287
+ await client.session.pairPhone("5491155554444", "123456");
288
+
289
+ // Request message history sync
290
+ await client.session.requestHistory();
291
+
292
+ // Configure proxy
293
+ await client.session.setProxy("socks5://user:pass@proxy:port");
294
+ ```
92
295
 
296
+ ### S3 Storage
297
+ ```typescript
93
298
  // Configure S3 storage
94
299
  await client.session.configureS3({
95
300
  enabled: true,
@@ -102,12 +307,23 @@ await client.session.configureS3({
102
307
  mediaDelivery: "both",
103
308
  retentionDays: 30,
104
309
  });
310
+
311
+ // Get S3 configuration
312
+ const s3Config = await client.session.getS3Config();
313
+
314
+ // Test S3 connection
315
+ await client.session.testS3();
316
+
317
+ // Delete S3 configuration
318
+ await client.session.deleteS3Config();
105
319
  ```
106
320
 
107
- ### Chat Module
321
+ </details>
108
322
 
109
- Send messages and manage chat interactions.
323
+ <details>
324
+ <summary><strong>💬 Chat Module</strong> - Send and manage messages</summary>
110
325
 
326
+ ### Basic Messages
111
327
  ```typescript
112
328
  // Send text message
113
329
  await client.chat.sendText({
@@ -125,47 +341,97 @@ await client.chat.sendText({
125
341
  Participant: "5491155553935@s.whatsapp.net",
126
342
  },
127
343
  });
344
+ ```
128
345
 
346
+ ### Media Messages
347
+ ```typescript
129
348
  // Send image
130
349
  await client.chat.sendImage({
131
350
  Phone: "5491155554444",
132
- Image: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...",
351
+ Image: "data:image/jpeg;base64,/9j/4AAQ...",
133
352
  Caption: "Check this out!",
134
353
  });
135
354
 
136
- // Send template message with buttons
137
- await client.chat.sendTemplate({
355
+ // Send audio
356
+ await client.chat.sendAudio({
357
+ Phone: "5491155554444",
358
+ Audio: "data:audio/ogg;base64,T2dnUw...",
359
+ });
360
+
361
+ // Send video
362
+ await client.chat.sendVideo({
363
+ Phone: "5491155554444",
364
+ Video: "data:video/mp4;base64,AAAAIGZ0eXA...",
365
+ Caption: "Video caption",
366
+ });
367
+
368
+ // Send document
369
+ await client.chat.sendDocument({
370
+ Phone: "5491155554444",
371
+ Document: "data:application/pdf;base64,JVBERi0x...",
372
+ FileName: "document.pdf",
373
+ });
374
+
375
+ // Send sticker
376
+ await client.chat.sendSticker({
377
+ Phone: "5491155554444",
378
+ Sticker: "data:image/webp;base64,UklGRuI...",
379
+ });
380
+ ```
381
+
382
+ ### Interactive Messages
383
+ ```typescript
384
+ // Send buttons
385
+ await client.chat.sendButtons({
138
386
  Phone: "5491155554444",
139
- Content: "Choose an option:",
140
- Footer: "Powered by WuzAPI",
387
+ Body: "Choose an option:",
388
+ Footer: "Select one:",
141
389
  Buttons: [
142
- { DisplayText: "Yes", Type: "quickreply" },
143
- { DisplayText: "No", Type: "quickreply" },
144
- { DisplayText: "Visit Site", Type: "url", Url: "https://example.com" },
145
- { DisplayText: "Call Us", Type: "call", PhoneNumber: "1155554444" },
390
+ { ButtonId: "option1", ButtonText: { DisplayText: "Option 1" }, Type: 1 },
391
+ { ButtonId: "option2", ButtonText: { DisplayText: "Option 2" }, Type: 1 },
146
392
  ],
147
393
  });
148
394
 
149
- // Send location
150
- await client.chat.sendLocation({
395
+ // Send list message
396
+ await client.chat.sendList({
151
397
  Phone: "5491155554444",
152
- Latitude: 48.85837,
153
- Longitude: 2.294481,
154
- Name: "Eiffel Tower, Paris",
398
+ Body: "Please select from the menu:",
399
+ Title: "Menu Options",
400
+ ButtonText: "View Menu",
401
+ Sections: [
402
+ {
403
+ Title: "Main Course",
404
+ Rows: [
405
+ { Title: "Pizza", Description: "Delicious pizza", RowId: "pizza" },
406
+ { Title: "Burger", Description: "Tasty burger", RowId: "burger" },
407
+ ],
408
+ },
409
+ ],
155
410
  });
156
411
 
157
- // Send contact
158
- await client.chat.sendContact({
412
+ // Send poll
413
+ await client.chat.sendPoll({
159
414
  Phone: "5491155554444",
160
- Name: "John Doe",
161
- Vcard:
162
- "BEGIN:VCARD\nVERSION:3.0\nN:Doe;John;;;\nFN:John Doe\nTEL:+1234567890\nEND:VCARD",
415
+ Name: "What's your favorite color?",
416
+ Options: [{ Name: "Red" }, { Name: "Blue" }, { Name: "Green" }],
417
+ SelectableCount: 1,
418
+ });
419
+ ```
420
+
421
+ ### Message Management
422
+ ```typescript
423
+ // Delete a message
424
+ await client.chat.deleteMessage({
425
+ Phone: "5491155554444",
426
+ Id: "message-id-to-delete",
427
+ Remote: true, // Delete for everyone
163
428
  });
164
429
 
165
- // Mark messages as read
166
- await client.chat.markRead({
167
- Id: ["message-id-1", "message-id-2"],
168
- Chat: "5491155553934@s.whatsapp.net",
430
+ // Edit a message
431
+ await client.chat.editMessage({
432
+ Phone: "5491155554444",
433
+ MessageId: "message-id-to-edit",
434
+ NewText: "This is the updated message text",
169
435
  });
170
436
 
171
437
  // React to message
@@ -175,6 +441,40 @@ await client.chat.react({
175
441
  Id: "message-id-to-react-to",
176
442
  });
177
443
 
444
+ // Mark messages as read
445
+ await client.chat.markRead({
446
+ Id: ["message-id-1", "message-id-2"],
447
+ Chat: "5491155553934@s.whatsapp.net",
448
+ });
449
+
450
+ // Send chat presence (typing indicator)
451
+ await client.chat.sendPresence({
452
+ Phone: "5491155554444",
453
+ State: "composing", // or "paused"
454
+ Media: "text",
455
+ });
456
+ ```
457
+
458
+ ### Location and Contacts
459
+ ```typescript
460
+ // Send location
461
+ await client.chat.sendLocation({
462
+ Phone: "5491155554444",
463
+ Latitude: 48.85837,
464
+ Longitude: 2.294481,
465
+ Name: "Eiffel Tower, Paris",
466
+ });
467
+
468
+ // Send contact
469
+ await client.chat.sendContact({
470
+ Phone: "5491155554444",
471
+ Name: "John Doe",
472
+ Vcard: "BEGIN:VCARD\nVERSION:3.0\nN:Doe;John;;;\nFN:John Doe\nTEL:+1234567890\nEND:VCARD",
473
+ });
474
+ ```
475
+
476
+ ### Media Download
477
+ ```typescript
178
478
  // Download media
179
479
  const media = await client.chat.downloadImage({
180
480
  Url: "https://mmg.whatsapp.net/d/f/...",
@@ -185,9 +485,10 @@ const media = await client.chat.downloadImage({
185
485
  });
186
486
  ```
187
487
 
188
- ### User Module
488
+ </details>
189
489
 
190
- Get information about WhatsApp users.
490
+ <details>
491
+ <summary><strong>👤 User Module</strong> - User information and contacts</summary>
191
492
 
192
493
  ```typescript
193
494
  // Check if numbers are WhatsApp users
@@ -201,12 +502,21 @@ const avatar = await client.user.getAvatar("5491155554444", true); // true for p
201
502
 
202
503
  // Get all contacts
203
504
  const contacts = await client.user.getContacts();
505
+
506
+ // Send user presence (online/offline status)
507
+ await client.user.sendPresence({
508
+ Phone: "5491155554444",
509
+ State: "available", // or "unavailable"
510
+ LastSeen: Date.now(),
511
+ });
204
512
  ```
205
513
 
206
- ### Group Module
514
+ </details>
207
515
 
208
- Manage WhatsApp groups.
516
+ <details>
517
+ <summary><strong>👥 Group Module</strong> - Group management</summary>
209
518
 
519
+ ### Basic Group Operations
210
520
  ```typescript
211
521
  // List all groups
212
522
  const groups = await client.group.list();
@@ -220,44 +530,76 @@ const newGroup = await client.group.create("My New Group", [
220
530
  // Get group info
221
531
  const groupInfo = await client.group.getInfo("120362023605733675@g.us");
222
532
 
533
+ // Leave a group
534
+ await client.group.leave("120362023605733675@g.us");
535
+ ```
536
+
537
+ ### Group Settings
538
+ ```typescript
223
539
  // Set group name
224
540
  await client.group.setName("120362023605733675@g.us", "New Group Name");
225
541
 
226
- // Set group photo (JPEG only)
227
- await client.group.setPhoto(
542
+ // Set group topic/description
543
+ await client.group.setTopic(
228
544
  "120362023605733675@g.us",
229
- "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ..."
545
+ "Welcome to our group! Please read the rules."
230
546
  );
231
547
 
232
- // Get invite link
233
- const invite = await client.group.getInviteLink("120362023605733675@g.us");
548
+ // Set group announcement setting (only admins can send messages)
549
+ await client.group.setAnnounce("120362023605733675@g.us", true);
234
550
 
235
551
  // Set group locked (only admins can modify info)
236
552
  await client.group.setLocked("120362023605733675@g.us", true);
237
553
 
238
554
  // Set disappearing messages
239
555
  await client.group.setEphemeral("120362023605733675@g.us", "24h"); // '24h', '7d', '90d', or 'off'
556
+ ```
557
+
558
+ ### Group Media
559
+ ```typescript
560
+ // Set group photo (JPEG only)
561
+ await client.group.setPhoto(
562
+ "120362023605733675@g.us",
563
+ "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ..."
564
+ );
240
565
 
241
566
  // Remove group photo
242
567
  await client.group.removePhoto("120362023605733675@g.us");
243
568
  ```
244
569
 
245
- ### Admin Module
570
+ ### Invites and Participants
571
+ ```typescript
572
+ // Get invite link
573
+ const invite = await client.group.getInviteLink("120362023605733675@g.us");
574
+
575
+ // Join a group using invite link
576
+ const joinResult = await client.group.join("https://chat.whatsapp.com/XXXXXXXXX");
246
577
 
247
- Manage users (requires admin token).
578
+ // Get group invite information
579
+ const inviteInfo = await client.group.getInviteInfo("https://chat.whatsapp.com/XXXXXXXXX");
248
580
 
249
- ```typescript
250
- // Create admin client
251
- const adminClient = new WuzapiClient({
252
- apiUrl: "http://localhost:8080",
253
- token: "your-admin-token",
254
- });
581
+ // Update group participants (add, remove, promote, demote)
582
+ await client.group.updateParticipants(
583
+ "120362023605733675@g.us",
584
+ "add", // "add", "remove", "promote", "demote"
585
+ ["5491155553936", "5491155553937"]
586
+ );
587
+ ```
588
+
589
+ </details>
255
590
 
591
+ <details>
592
+ <summary><strong>👨‍💼 Admin Module</strong> - User management (requires admin token)</summary>
593
+
594
+ ```typescript
256
595
  // List all users
257
- const users = await adminClient.admin.listUsers();
596
+ const users = await client.admin.listUsers({ token: "admin-token" });
597
+
598
+ // Get a specific user by ID
599
+ const user = await client.admin.getUser(2, { token: "admin-token" });
258
600
 
259
601
  // Add new user
260
- const newUser = await adminClient.admin.addUser({
602
+ const newUser = await client.admin.addUser({
261
603
  name: "John Doe",
262
604
  token: "user-token-123",
263
605
  webhook: "https://example.com/webhook",
@@ -277,15 +619,19 @@ const newUser = await adminClient.admin.addUser({
277
619
  mediaDelivery: "both",
278
620
  retentionDays: 30,
279
621
  },
280
- });
622
+ }, { token: "admin-token" });
281
623
 
282
624
  // Delete user
283
- await adminClient.admin.deleteUser(2);
625
+ await client.admin.deleteUser(2, { token: "admin-token" });
626
+
627
+ // Delete user completely (full deletion including all data)
628
+ await client.admin.deleteUserComplete(2, { token: "admin-token" });
284
629
  ```
285
630
 
286
- ### Webhook Module
631
+ </details>
287
632
 
288
- Configure webhook settings and handle incoming WhatsApp events.
633
+ <details>
634
+ <summary><strong>🔗 Webhook Module</strong> - Webhook configuration</summary>
289
635
 
290
636
  ```typescript
291
637
  // Set webhook URL
@@ -295,134 +641,44 @@ await client.webhook.setWebhook("https://my-server.com/webhook");
295
641
  const webhookConfig = await client.webhook.getWebhook();
296
642
  console.log("Webhook URL:", webhookConfig.webhook);
297
643
  console.log("Subscribed events:", webhookConfig.subscribe);
298
- ```
299
644
 
300
- ## Webhook Event Handling
645
+ // Update webhook URL
646
+ await client.webhook.updateWebhook("https://my-new-server.com/webhook");
301
647
 
302
- WuzAPI sends real-time events to your webhook endpoint. Here's how to handle different types of events:
303
-
304
- ## Webhook Payload Structure
648
+ // Delete webhook configuration
649
+ await client.webhook.deleteWebhook();
650
+ ```
305
651
 
306
- When S3 is enabled, webhook payloads will include S3 information based on the `media_delivery` setting configured in your WuzAPI instance.
652
+ </details>
307
653
 
308
- ### Standard Event Payload
654
+ <details>
655
+ <summary><strong>📰 Newsletter Module</strong> - Newsletter management (Business accounts only)</summary>
309
656
 
310
- ```json
311
- {
312
- "event": {
313
- "Info": {
314
- "ID": "3EB06F9067F80BAB89FF",
315
- "Type": "text",
316
- "PushName": "John Doe",
317
- "Timestamp": "2024-12-25T10:30:00Z",
318
- "Source": {
319
- "Chat": "5491155553934@s.whatsapp.net",
320
- "Sender": "5491155553934@s.whatsapp.net",
321
- "IsFromMe": false,
322
- "IsGroup": false
323
- }
324
- },
325
- "Message": {
326
- "conversation": "Hello, this is a test message!"
327
- },
328
- "IsEphemeral": false,
329
- "IsViewOnce": false,
330
- "IsDocumentWithCaption": false,
331
- "IsEdit": false
332
- }
333
- }
657
+ ```typescript
658
+ // List all subscribed newsletters
659
+ const newsletters = await client.newsletter.list();
660
+
661
+ newsletters.Newsletters.forEach((newsletter) => {
662
+ console.log(`Newsletter: ${newsletter.Name}`);
663
+ console.log(`Description: ${newsletter.Description}`);
664
+ console.log(`Handle: ${newsletter.Handle}`);
665
+ console.log(`State: ${newsletter.State}`);
666
+ });
334
667
  ```
335
668
 
336
- ### S3 Only (`media_delivery: "s3"`)
337
-
338
- ```json
339
- {
340
- "event": {
341
- "Info": {
342
- "ID": "3EB06F9067F80BAB89FF",
343
- "Type": "image",
344
- "PushName": "John Doe",
345
- "Timestamp": "2024-12-25T10:30:00Z",
346
- "Source": {
347
- "Chat": "5491155553934@s.whatsapp.net",
348
- "Sender": "5491155553934@s.whatsapp.net",
349
- "IsFromMe": false,
350
- "IsGroup": false
351
- }
352
- },
353
- "Message": {
354
- "imageMessage": {
355
- "caption": "Check out this photo!",
356
- "mimetype": "image/jpeg",
357
- "width": 1920,
358
- "height": 1080,
359
- "fileLength": 245632
360
- }
361
- },
362
- "IsEphemeral": false,
363
- "IsViewOnce": false
364
- },
365
- "s3": {
366
- "url": "https://my-bucket.s3.us-east-1.amazonaws.com/users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
367
- "key": "users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
368
- "bucket": "my-bucket",
369
- "size": 245632,
370
- "mimeType": "image/jpeg",
371
- "fileName": "3EB06F9067F80BAB89FF.jpg"
372
- }
373
- }
374
- ```
669
+ </details>
375
670
 
376
- ### Both S3 and Base64 (`media_delivery: "both"`)
377
-
378
- ```json
379
- {
380
- "event": {
381
- "Info": { "..." },
382
- "Message": {
383
- "imageMessage": {
384
- "caption": "Check out this photo!",
385
- "mimetype": "image/jpeg",
386
- "width": 1920,
387
- "height": 1080
388
- }
389
- }
390
- },
391
- "base64": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=",
392
- "mimeType": "image/jpeg",
393
- "fileName": "3EB06F9067F80BAB89FF.jpg",
394
- "s3": {
395
- "url": "https://my-bucket.s3.us-east-1.amazonaws.com/users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
396
- "key": "users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
397
- "bucket": "my-bucket",
398
- "size": 245632,
399
- "mimeType": "image/jpeg",
400
- "fileName": "3EB06F9067F80BAB89FF.jpg"
401
- }
402
- }
403
- ```
671
+ ---
404
672
 
405
- ### Base64 Only (`media_delivery: "base64"`)
673
+ ## 🎣 Webhook Event Handling
406
674
 
407
- ```json
408
- {
409
- "event": { "..." },
410
- "base64": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=",
411
- "mimeType": "image/jpeg",
412
- "fileName": "3EB06F9067F80BAB89FF.jpg"
413
- }
414
- ```
675
+ WuzAPI sends real-time events to your webhook endpoint. Here's how to handle them:
415
676
 
416
677
  ### Basic Webhook Setup
417
678
 
418
679
  ```typescript
419
680
  import express from "express";
420
- import WuzapiClient, {
421
- EventType,
422
- hasS3Media,
423
- hasBase64Media,
424
- WebhookPayload,
425
- } from "wuzapi";
681
+ import WuzapiClient, { getMessageContent, hasS3Media } from "wuzapi";
426
682
 
427
683
  const app = express();
428
684
  app.use(express.json());
@@ -432,787 +688,73 @@ const client = new WuzapiClient({
432
688
  token: "your-token",
433
689
  });
434
690
 
435
- // Webhook endpoint with S3 media support
436
691
  app.post("/webhook", async (req, res) => {
437
692
  try {
438
- const webhookPayload: WebhookPayload = req.body;
439
-
440
- // Handle S3 media information if present
693
+ const webhookPayload = req.body;
694
+
695
+ // Handle S3 media if present
441
696
  if (hasS3Media(webhookPayload)) {
442
- console.log("☁️ S3 Media:", {
443
- url: webhookPayload.s3.url,
444
- bucket: webhookPayload.s3.bucket,
445
- size: webhookPayload.s3.size,
446
- type: webhookPayload.s3.mimeType,
447
- });
448
- }
449
-
450
- if (hasBase64Media(webhookPayload)) {
451
- console.log("📦 Base64 media included");
452
- // Process base64 media: webhookPayload.base64
697
+ console.log("S3 Media:", webhookPayload.s3.url);
453
698
  }
454
-
455
- // Extract the actual event data
699
+
456
700
  const event = webhookPayload.event || webhookPayload;
457
- const eventType = detectEventType(event);
458
-
459
- // Handle different event types using EventType enum
460
- switch (eventType) {
461
- case EventType.MESSAGE:
462
- await handleMessage(event);
463
- break;
464
- case EventType.RECEIPT:
465
- await handleReceipt(event);
466
- break;
467
- case EventType.PRESENCE:
468
- await handlePresence(event);
469
- break;
470
- default:
471
- console.log(`Unhandled event type: ${eventType}`);
701
+
702
+ // Handle messages
703
+ if (event.Message && event.Info) {
704
+ const messageContent = getMessageContent(event.Message);
705
+ const from = event.Info.RemoteJid.replace("@s.whatsapp.net", "");
706
+
707
+ if (messageContent?.type === "text") {
708
+ console.log(`Message from ${from}: ${messageContent.content}`);
709
+
710
+ // Auto-reply
711
+ if (messageContent.content.toLowerCase().includes("hello")) {
712
+ await client.chat.sendText({
713
+ Phone: from,
714
+ Body: "Hello! 👋 How can I help you?",
715
+ });
716
+ }
717
+ }
472
718
  }
473
-
474
- res.status(200).json({ success: true });
719
+
720
+ res.json({ success: true });
475
721
  } catch (error) {
476
722
  console.error("Webhook error:", error);
477
723
  res.status(500).json({ error: error.message });
478
724
  }
479
725
  });
726
+ ```
480
727
 
481
- // Helper function to detect event type
482
- function detectEventType(event: any): EventType | string {
483
- if (event.Message && event.Info) return EventType.MESSAGE;
484
- if (event.MessageIDs && event.Type) return EventType.RECEIPT;
485
- if (event.From && typeof event.Unavailable !== "undefined")
486
- return EventType.PRESENCE;
487
- if (event.State && event.MessageSource) return EventType.CHAT_PRESENCE;
488
- if (event.Codes) return EventType.QR;
489
- // Add more detection logic as needed
490
- return "Unknown";
491
- }
728
+ ### Message Types
492
729
 
493
- app.listen(3000, () => {
494
- console.log("Webhook server running on port 3000");
495
- });
496
- ```
497
-
498
- ### Message Events
499
-
500
- Handle incoming messages of all types using the comprehensive Message types:
501
-
502
- ```typescript
503
- import { getMessageContent } from "wuzapi";
504
-
505
- async function handleMessage(event) {
506
- const { Info, Message, IsEphemeral, IsViewOnce } = event;
507
- const from = Info.RemoteJid.replace("@s.whatsapp.net", "");
508
- const isGroup = Info.RemoteJid.includes("@g.us");
509
-
510
- console.log(`New message from ${from}${isGroup ? " (group)" : ""}`);
511
-
512
- if (IsEphemeral) console.log("⏱️ Ephemeral message");
513
- if (IsViewOnce) console.log("👁️ View once message");
514
-
515
- // Use the utility function to get structured message content
516
- const messageContent = getMessageContent(Message);
517
-
518
- if (!messageContent) {
519
- console.log("❓ Unknown message type");
520
- return;
521
- }
522
-
523
- // Handle different message types with full type safety
524
- switch (messageContent.type) {
525
- case "text":
526
- console.log(`💬 Text: ${messageContent.content}`);
527
-
528
- // Auto-reply to specific messages
529
- if (messageContent.content.toLowerCase().includes("hello")) {
530
- await client.chat.sendText({
531
- Phone: from,
532
- Body: "👋 Hello! Thanks for your message.",
533
- });
534
- }
535
- break;
536
-
537
- case "extendedText":
538
- console.log(`📝 Extended text: ${messageContent.content.text}`);
539
- if (messageContent.content.canonicalUrl) {
540
- console.log(`🔗 Link preview: ${messageContent.content.canonicalUrl}`);
541
- console.log(`📰 Title: ${messageContent.content.title}`);
542
- }
543
- break;
544
-
545
- case "image":
546
- const imageMsg = messageContent.content;
547
- console.log(`🖼️ Image: ${imageMsg.caption || "No caption"}`);
548
- console.log(`📏 Dimensions: ${imageMsg.width}x${imageMsg.height}`);
549
-
550
- // Download the image with proper types
551
- if (imageMsg.url && imageMsg.mediaKey) {
552
- try {
553
- const media = await client.chat.downloadImage({
554
- Url: imageMsg.url,
555
- MediaKey: Array.from(imageMsg.mediaKey),
556
- Mimetype: imageMsg.mimetype,
557
- FileSHA256: Array.from(imageMsg.fileSha256),
558
- FileLength: imageMsg.fileLength,
559
- });
560
- console.log("✅ Image downloaded successfully");
561
- } catch (error) {
562
- console.log("❌ Failed to download image:", error.message);
563
- }
564
- }
565
- break;
566
-
567
- case "video":
568
- const videoMsg = messageContent.content;
569
- console.log(`🎥 Video: ${videoMsg.caption || "No caption"}`);
570
- console.log(`⏱️ Duration: ${videoMsg.seconds}s`);
571
- if (videoMsg.gifPlayback) {
572
- console.log("🎬 GIF playback enabled");
573
- }
574
- break;
575
-
576
- case "audio":
577
- const audioMsg = messageContent.content;
578
- console.log(`🎵 Audio: ${audioMsg.seconds}s`);
579
- if (audioMsg.ptt) {
580
- console.log("🎤 Voice note (Push-to-talk)");
581
- }
582
- break;
583
-
584
- case "document":
585
- const docMsg = messageContent.content;
586
- console.log(`📄 Document: ${docMsg.fileName || docMsg.title}`);
587
- console.log(`📊 Size: ${docMsg.fileLength} bytes`);
588
- console.log(`📋 Type: ${docMsg.mimetype}`);
589
- break;
590
-
591
- case "location":
592
- const locMsg = messageContent.content;
593
- console.log(
594
- `📍 Location: ${locMsg.degreesLatitude}, ${locMsg.degreesLongitude}`
595
- );
596
- if (locMsg.name) console.log(`🏷️ Name: ${locMsg.name}`);
597
- if (locMsg.isLiveLocation) console.log("📡 Live location sharing");
598
- break;
599
-
600
- case "contact":
601
- console.log(`👤 Contact: ${messageContent.content.displayName}`);
602
- break;
603
-
604
- case "sticker":
605
- const stickerMsg = messageContent.content;
606
- console.log(`😀 Sticker`);
607
- if (stickerMsg.isAnimated) console.log("🎬 Animated");
608
- if (stickerMsg.isLottie) console.log("🎨 Lottie");
609
- break;
610
-
611
- case "buttons":
612
- const btnMsg = messageContent.content;
613
- console.log(`🔘 Interactive buttons: ${btnMsg.contentText}`);
614
- console.log(`🔢 ${btnMsg.buttons?.length || 0} buttons`);
615
- break;
616
-
617
- case "list":
618
- const listMsg = messageContent.content;
619
- console.log(`📋 List: ${listMsg.title}`);
620
- console.log(`📝 ${listMsg.sections?.length || 0} sections`);
621
- break;
622
-
623
- case "buttonsResponse":
624
- const btnResponse = messageContent.content;
625
- console.log(`✅ Button clicked: ${btnResponse.selectedDisplayText}`);
626
- console.log(`🆔 Button ID: ${btnResponse.selectedButtonId}`);
627
-
628
- // Handle button responses
629
- switch (btnResponse.selectedButtonId) {
630
- case "help":
631
- await client.chat.sendText({
632
- Phone: from,
633
- Body: "🆘 How can I help you?",
634
- });
635
- break;
636
- case "info":
637
- await client.chat.sendText({
638
- Phone: from,
639
- Body: "ℹ️ Here's some information...",
640
- });
641
- break;
642
- }
643
- break;
644
-
645
- case "listResponse":
646
- const listResponse = messageContent.content;
647
- console.log(`✅ List selection: ${listResponse.title}`);
648
- if (listResponse.singleSelectReply) {
649
- console.log(
650
- `🆔 Selected: ${listResponse.singleSelectReply.selectedRowId}`
651
- );
652
- }
653
- break;
654
-
655
- case "poll":
656
- const pollMsg = messageContent.content;
657
- console.log(`📊 Poll: ${pollMsg.name}`);
658
- console.log(`🔢 Options: ${pollMsg.options?.length || 0}`);
659
- break;
660
-
661
- case "reaction":
662
- const reactionMsg = messageContent.content;
663
- console.log(`😊 Reaction: ${reactionMsg.text}`);
664
- console.log(`📝 To message: ${reactionMsg.key?.id}`);
665
- break;
666
-
667
- case "groupInvite":
668
- const inviteMsg = messageContent.content;
669
- console.log(`👥 Group invite: ${inviteMsg.groupName}`);
670
- console.log(`🔗 Code: ${inviteMsg.inviteCode}`);
671
- break;
672
-
673
- default:
674
- console.log(`❓ Unhandled message type: ${messageContent.type}`);
675
- }
676
- }
677
- ```
678
-
679
- ### Read Receipts and Delivery Confirmations
680
-
681
- Handle message delivery and read confirmations:
682
-
683
- ```typescript
684
- async function handleReceipt(event) {
685
- const { MessageSource, MessageIDs, Type, Timestamp } = event;
686
- const from = MessageSource.Chat.User;
687
-
688
- console.log(
689
- `Receipt from ${from}: ${Type} for ${MessageIDs.length} message(s)`
690
- );
691
-
692
- switch (Type) {
693
- case "delivery":
694
- console.log(`✅ Messages delivered to ${from}`);
695
- // Update your database to mark messages as delivered
696
- break;
697
-
698
- case "read":
699
- console.log(`👀 Messages read by ${from}`);
700
- // Update your database to mark messages as read
701
- break;
702
-
703
- case "played":
704
- console.log(`▶️ Voice/video messages played by ${from}`);
705
- // Update your database to mark media as played
706
- break;
707
- }
708
- }
709
- ```
710
-
711
- ### Presence and Typing Indicators
712
-
713
- Handle user online/offline status and typing indicators:
714
-
715
- ```typescript
716
- // User online/offline status
717
- async function handlePresence(event) {
718
- const { From, Unavailable, LastSeen } = event;
719
- const user = From.User;
720
-
721
- if (Unavailable) {
722
- console.log(`🔴 ${user} went offline (last seen: ${LastSeen})`);
723
- } else {
724
- console.log(`🟢 ${user} is online`);
725
- }
726
- }
727
-
728
- // Typing indicators
729
- async function handleChatPresence(event) {
730
- const { MessageSource, State, Media } = event;
731
- const from = MessageSource.Sender.User;
732
- const isGroup = MessageSource.IsGroup;
733
-
734
- if (State === "composing") {
735
- if (Media === "text") {
736
- console.log(`⌨️ ${from} is typing${isGroup ? " in group" : ""}...`);
737
- } else {
738
- console.log(
739
- `📎 ${from} is sending ${Media}${isGroup ? " in group" : ""}...`
740
- );
741
- }
742
- } else if (State === "paused") {
743
- console.log(`⏸️ ${from} stopped typing`);
744
- }
745
- }
746
- ```
747
-
748
- ### Group Events
749
-
750
- Handle group-related events:
751
-
752
- ```typescript
753
- // Group info updates
754
- async function handleGroupInfo(event) {
755
- const { JID, GroupName, Participants, Sender } = event;
756
- console.log(`👥 Group info updated for ${GroupName} (${JID.User})`);
757
- console.log(`👤 Updated by: ${Sender.User}`);
758
- console.log(`👥 Participants: ${Participants.length}`);
759
- }
760
-
761
- // When you're added to a group
762
- async function handleJoinedGroup(event) {
763
- const { Reason, Type, Participants } = event;
764
- console.log(`🎉 Joined group! Reason: ${Reason}, Type: ${Type}`);
765
- console.log(`👥 Group has ${Participants.length} participants`);
766
-
767
- // Send welcome message
768
- // Note: You'll need the group JID from the event context
769
- }
770
- ```
771
-
772
- ### Connection Events
773
-
774
- Handle connection status changes:
775
-
776
- ```typescript
777
- async function handleConnected(event) {
778
- console.log("🟢 Connected to WhatsApp!");
779
- // Your bot is now ready to send messages
780
- }
781
-
782
- async function handleDisconnected(event) {
783
- console.log("🔴 Disconnected from WhatsApp");
784
- // Attempt to reconnect or notify administrators
785
- }
786
-
787
- async function handleLoggedOut(event) {
788
- const { Reason, OnConnect } = event;
789
- console.log(`🚪 Logged out from WhatsApp. Reason: ${Reason}`);
790
-
791
- if (OnConnect) {
792
- console.log("⚠️ Logout occurred during connection");
793
- }
794
-
795
- // You may need to scan QR code again
796
- }
797
- ```
798
-
799
- ### QR Code Events
800
-
801
- Handle QR code generation for initial setup:
802
-
803
- ```typescript
804
- async function handleQR(event) {
805
- const { Codes } = event;
806
- console.log("📱 New QR codes received:");
807
-
808
- Codes.forEach((code, index) => {
809
- console.log(`📷 QR Code ${index + 1}: ${code}`);
810
- // Display QR code to user or save to file
811
- });
812
- }
813
- ```
814
-
815
- ### Profile and Contact Updates
816
-
817
- Handle profile picture and contact information changes:
818
-
819
- ```typescript
820
- // Profile picture changes
821
- async function handlePicture(event) {
822
- const { JID, Author, Remove, Timestamp } = event;
823
- const target = JID.User;
824
- const changer = Author.User;
825
-
826
- if (Remove) {
827
- console.log(`🗑️ ${changer} removed profile picture for ${target}`);
828
- } else {
829
- console.log(`🖼️ ${changer} updated profile picture for ${target}`);
830
- }
831
- }
832
-
833
- // Contact info updates
834
- async function handleContact(event) {
835
- const { JID, Found, FullName, PushName, BusinessName } = event;
836
- console.log(`👤 Contact info: ${FullName || PushName} (${JID.User})`);
837
-
838
- if (BusinessName) {
839
- console.log(`🏢 Business: ${BusinessName}`);
840
- }
841
-
842
- console.log(`✅ Found in WhatsApp: ${Found}`);
843
- }
844
-
845
- // Name changes
846
- async function handlePushName(event) {
847
- const { JID, OldPushName, NewPushName } = event;
848
- console.log(
849
- `📝 ${JID.User} changed name from "${OldPushName}" to "${NewPushName}"`
850
- );
851
- }
852
- ```
853
-
854
- ### Error Handling
855
-
856
- Handle various error events:
857
-
858
- ```typescript
859
- // Undecryptable messages
860
- async function handleUndecryptableMessage(event) {
861
- const { Info, IsUnavailable, UnavailableType, DecryptFailMode } = event;
862
- console.log(`❌ Failed to decrypt message from ${Info.Source.Sender.User}`);
863
- console.log(
864
- `📊 Unavailable: ${IsUnavailable}, Type: ${UnavailableType}, Mode: ${DecryptFailMode}`
865
- );
866
-
867
- // Log for debugging or request message retry
868
- }
869
-
870
- // Stream errors
871
- async function handleStreamError(event) {
872
- const { Code } = event;
873
- console.log(`🚨 Stream error: ${Code}`);
874
-
875
- // Handle specific error codes
876
- switch (Code) {
877
- case "conflict":
878
- console.log("Another client connected with same credentials");
879
- break;
880
- case "stream:error":
881
- console.log("General stream error occurred");
882
- break;
883
- default:
884
- console.log("Unknown stream error");
885
- }
886
- }
887
- ```
888
-
889
- ### Complete Webhook Server Example
890
-
891
- Here's a complete webhook server that handles all event types:
892
-
893
- ```typescript
894
- import express from "express";
895
- import WuzapiClient, {
896
- Message,
897
- Receipt,
898
- Presence,
899
- EventGroupInfo,
900
- } from "wuzapi";
901
-
902
- const app = express();
903
- app.use(express.json());
904
-
905
- const client = new WuzapiClient({
906
- apiUrl: process.env.WUZAPI_URL || "http://localhost:8080",
907
- token: process.env.WUZAPI_TOKEN || "your-token",
908
- });
909
-
910
- // Event router
911
- const eventHandlers = {
912
- Message: handleMessage,
913
- Receipt: handleReceipt,
914
- Presence: handlePresence,
915
- ChatPresence: handleChatPresence,
916
- GroupInfo: handleGroupInfo,
917
- JoinedGroup: handleJoinedGroup,
918
- Connected: handleConnected,
919
- Disconnected: handleDisconnected,
920
- LoggedOut: handleLoggedOut,
921
- QR: handleQR,
922
- Picture: handlePicture,
923
- Contact: handleContact,
924
- PushName: handlePushName,
925
- UndecryptableMessage: handleUndecryptableMessage,
926
- StreamError: handleStreamError,
927
- };
928
-
929
- app.post("/webhook", async (req, res) => {
930
- try {
931
- const eventData = req.body;
932
-
933
- // Log all events for debugging
934
- console.log("📨 Webhook received:", JSON.stringify(eventData, null, 2));
935
-
936
- // Route to appropriate handler based on event structure
937
- for (const [eventType, handler] of Object.entries(eventHandlers)) {
938
- if (isEventType(eventData, eventType)) {
939
- await handler(eventData);
940
- break;
941
- }
942
- }
943
-
944
- res.status(200).json({ success: true });
945
- } catch (error) {
946
- console.error("❌ Webhook processing error:", error);
947
- res.status(500).json({ error: error.message });
948
- }
949
- });
950
-
951
- // Helper function to identify event types
952
- function isEventType(event, type) {
953
- switch (type) {
954
- case "Message":
955
- return event.Message && event.Info;
956
- case "Receipt":
957
- return event.MessageIDs && event.Type;
958
- case "Presence":
959
- return event.From && typeof event.Unavailable !== "undefined";
960
- case "ChatPresence":
961
- return event.State && event.MessageSource;
962
- case "GroupInfo":
963
- return event.GroupName && event.Participants;
964
- case "QR":
965
- return event.Codes;
966
- // Add more type checks as needed
967
- default:
968
- return false;
969
- }
970
- }
971
-
972
- // Health check
973
- app.get("/health", (req, res) => {
974
- res.json({ status: "healthy", timestamp: new Date().toISOString() });
975
- });
976
-
977
- // Initialize
978
- async function initialize() {
979
- try {
980
- // Connect to WhatsApp with all events using EventType enum
981
- await client.session.connect({
982
- Subscribe: [
983
- EventType.MESSAGE,
984
- EventType.RECEIPT,
985
- EventType.PRESENCE,
986
- EventType.CHAT_PRESENCE,
987
- EventType.GROUP_INFO,
988
- EventType.CONTACT,
989
- EventType.PUSH_NAME,
990
- EventType.PICTURE,
991
- EventType.QR,
992
- EventType.CONNECTED,
993
- EventType.DISCONNECTED,
994
- EventType.LOGGED_OUT,
995
- EventType.UNDECRYPTABLE_MESSAGE,
996
- EventType.STREAM_ERROR,
997
- ],
998
- Immediate: false,
999
- });
1000
-
1001
- // Set webhook
1002
- const webhookUrl =
1003
- process.env.WEBHOOK_URL || "http://localhost:3000/webhook";
1004
- await client.webhook.setWebhook(webhookUrl);
1005
-
1006
- app.listen(3000, () => {
1007
- console.log("🚀 Webhook server running on port 3000");
1008
- console.log("🎉 Ready to receive WhatsApp events!");
1009
- });
1010
- } catch (error) {
1011
- console.error("❌ Initialization failed:", error);
1012
- process.exit(1);
1013
- }
1014
- }
1015
-
1016
- initialize();
1017
- ```
1018
-
1019
- ### Event Types Reference
1020
-
1021
- All webhook events are fully typed. Import the specific event types you need:
730
+ The `getMessageContent()` utility function returns structured message data:
1022
731
 
1023
732
  ```typescript
1024
- import {
1025
- // Event type enum for all possible events
1026
- EventType,
1027
-
1028
- // Core message types
1029
- Message,
1030
- MessageEvent,
1031
- MessageContent,
1032
- getMessageContent,
1033
-
1034
- // Specific message types
1035
- ImageMessage,
1036
- VideoMessage,
1037
- AudioMessage,
1038
- DocumentMessage,
1039
- LocationMessage,
1040
- ContactMessage,
1041
- StickerMessage,
1042
- ButtonsMessage,
1043
- ListMessage,
1044
- PollCreationMessage,
1045
- ReactionMessage,
1046
-
1047
- // Webhook payload types
1048
- WebhookPayload,
1049
- S3MediaInfo,
1050
- S3OnlyWebhookPayload,
1051
- Base64OnlyWebhookPayload,
1052
- BothMediaWebhookPayload,
1053
- hasS3Media,
1054
- hasBase64Media,
1055
- hasBothMedia,
1056
-
1057
- // Event types
1058
- Receipt,
1059
- Presence,
1060
- ChatPresence,
1061
- EventGroupInfo,
1062
- EventContact,
1063
- QR,
1064
- Picture,
1065
- PushName,
1066
- Connected,
1067
- Disconnected,
1068
- LoggedOut,
1069
- UndecryptableMessage,
1070
- StreamError,
1071
- WhatsAppEvent,
1072
- } from "wuzapi";
1073
-
1074
- // Type-safe event handling with EventType enum
1075
- async function handleTypedWebhook(webhookPayload: WebhookPayload) {
1076
- const event = webhookPayload.event;
1077
-
1078
- // Type-safe event detection using EventType enum
1079
- const eventType = detectEventType(event);
1080
-
1081
- switch (eventType) {
1082
- case EventType.MESSAGE:
1083
- const messageEvent = event as MessageEvent;
1084
- const messageContent = getMessageContent(messageEvent.Message);
1085
-
1086
- if (messageContent?.type === "image") {
1087
- // TypeScript knows this is an ImageMessage
1088
- const imageMsg: ImageMessage = messageContent.content;
1089
- console.log(`Image dimensions: ${imageMsg.width}x${imageMsg.height}`);
1090
-
1091
- // Handle S3 media if available
1092
- if (hasS3Media(webhookPayload)) {
1093
- console.log(`S3 URL: ${webhookPayload.s3.url}`);
1094
- // Download from S3 or process as needed
1095
- }
1096
-
1097
- // Handle Base64 media if available
1098
- if (hasBase64Media(webhookPayload)) {
1099
- console.log("Processing base64 image data");
1100
- // Process base64 data: webhookPayload.base64
1101
- }
1102
- }
1103
- break;
1104
-
1105
- case EventType.RECEIPT:
1106
- const receiptEvent = event as Receipt;
1107
- console.log(`Receipt type: ${receiptEvent.Type}`);
1108
- break;
1109
-
1110
- case EventType.PRESENCE:
1111
- const presenceEvent = event as Presence;
1112
- console.log(`User ${presenceEvent.From.User} presence update`);
1113
- break;
1114
-
1115
- default:
1116
- console.log(`Unhandled event: ${eventType}`);
1117
- }
1118
- }
733
+ const messageContent = getMessageContent(event.Message);
1119
734
 
1120
- // Complete webhook server with S3 support
1121
- app.post("/webhook", async (req, res) => {
1122
- try {
1123
- const payload: WebhookPayload = req.body;
1124
- await handleTypedWebhook(payload);
1125
- res.status(200).json({ success: true });
1126
- } catch (error) {
1127
- console.error("Webhook error:", error);
1128
- res.status(500).json({ error: error.message });
1129
- }
1130
- });
1131
-
1132
- // Helper function to handle all message types generically
1133
- function processMessageContent(content: MessageContent) {
1134
- switch (content.type) {
1135
- case "text":
1136
- // content.content is string
1137
- return content.content.toUpperCase();
1138
-
1139
- case "image":
1140
- // content.content is ImageMessage
1141
- return `Image: ${content.content.caption || "No caption"}`;
1142
-
1143
- case "video":
1144
- // content.content is VideoMessage
1145
- return `Video (${content.content.seconds}s): ${
1146
- content.content.caption || "No caption"
1147
- }`;
1148
-
1149
- // TypeScript ensures you handle all possible types
1150
- default:
1151
- return `Unknown message type: ${content.type}`;
1152
- }
735
+ switch (messageContent?.type) {
736
+ case "text":
737
+ console.log("Text:", messageContent.content);
738
+ break;
739
+ case "image":
740
+ console.log("Image:", messageContent.content.caption);
741
+ break;
742
+ case "buttonsResponse":
743
+ console.log("Button clicked:", messageContent.content.selectedButtonId);
744
+ break;
745
+ case "listResponse":
746
+ console.log("List selection:", messageContent.content.singleSelectReply?.selectedRowId);
747
+ break;
748
+ // ... handle other types
1153
749
  }
1154
750
  ```
1155
751
 
1156
- ### Advanced Message Type Handling
1157
-
1158
- For complex message processing, you can work directly with the typed message structures:
1159
-
1160
- ```typescript
1161
- import {
1162
- Message,
1163
- ExtendedTextMessage,
1164
- ButtonsMessage,
1165
- ContextInfo,
1166
- } from "wuzapi";
1167
-
1168
- // Check for extended text with link preview
1169
- function hasLinkPreview(message: Message): boolean {
1170
- return !!message.extendedTextMessage?.canonicalUrl;
1171
- }
1172
-
1173
- // Extract context info from any message
1174
- function getMessageContext(message: Message): ContextInfo | undefined {
1175
- // Check various message types for context info
1176
- return (
1177
- message.extendedTextMessage?.contextInfo ||
1178
- message.imageMessage?.contextInfo ||
1179
- message.videoMessage?.contextInfo ||
1180
- message.audioMessage?.contextInfo
1181
- );
1182
- }
1183
-
1184
- // Process interactive messages
1185
- function handleInteractiveMessage(message: Message) {
1186
- if (message.buttonsMessage) {
1187
- const btns = message.buttonsMessage;
1188
- console.log(`Interactive message: ${btns.contentText}`);
1189
-
1190
- btns.buttons?.forEach((button) => {
1191
- if (button.type === ButtonType.RESPONSE) {
1192
- console.log(`Response button: ${button.buttonText?.displayText}`);
1193
- } else if (button.type === ButtonType.NATIVE_FLOW) {
1194
- console.log(`Native flow: ${button.nativeFlowInfo?.name}`);
1195
- }
1196
- });
1197
- }
1198
-
1199
- if (message.listMessage) {
1200
- const list = message.listMessage;
1201
- console.log(`List: ${list.title}`);
1202
-
1203
- list.sections?.forEach((section) => {
1204
- console.log(`Section: ${section.title}`);
1205
- section.rows?.forEach((row) => {
1206
- console.log(`- ${row.title}: ${row.description}`);
1207
- });
1208
- });
1209
- }
1210
- }
1211
- ```
752
+ ---
1212
753
 
1213
- ## Error Handling
754
+ ## 🛠️ Advanced Topics
1214
755
 
1215
- The library provides comprehensive error handling with detailed error information:
756
+ <details>
757
+ <summary><strong>⚠️ Error Handling</strong></summary>
1216
758
 
1217
759
  ```typescript
1218
760
  import { WuzapiError } from "wuzapi";
@@ -1235,16 +777,37 @@ try {
1235
777
  }
1236
778
  ```
1237
779
 
1238
- ### Error Types
780
+ ### Common Error Codes
781
+ - **401**: Authentication required
782
+ - **404**: Endpoint not found
783
+ - **500**: Server error
1239
784
 
1240
- - **Network Errors**: Connection issues, timeouts
1241
- - **Authentication Errors**: Invalid tokens, permission denied
1242
- - **API Errors**: Invalid parameters, service unavailable
1243
- - **Validation Errors**: Missing required fields, invalid data formats
785
+ </details>
1244
786
 
1245
- ## TypeScript Support
787
+ <details>
788
+ <summary><strong>🔧 Custom Configuration</strong></summary>
789
+
790
+ ```typescript
791
+ // Custom axios configuration
792
+ import { BaseClient } from "wuzapi";
793
+
794
+ class CustomClient extends BaseClient {
795
+ constructor(config) {
796
+ super(config);
797
+
798
+ // Add custom interceptors
799
+ this.axios.interceptors.request.use((config) => {
800
+ console.log("Making request:", config.url);
801
+ return config;
802
+ });
803
+ }
804
+ }
805
+ ```
1246
806
 
1247
- The library is built with TypeScript and provides complete type definitions:
807
+ </details>
808
+
809
+ <details>
810
+ <summary><strong>📝 TypeScript Support</strong></summary>
1248
811
 
1249
812
  ```typescript
1250
813
  import {
@@ -1264,9 +827,10 @@ const request: SendTextRequest = {
1264
827
  const response: SendMessageResponse = await client.chat.sendText(request);
1265
828
  ```
1266
829
 
1267
- ## Legacy Aliases
830
+ </details>
1268
831
 
1269
- For convenience, the library provides some legacy aliases:
832
+ <details>
833
+ <summary><strong>🔄 Legacy Aliases</strong></summary>
1270
834
 
1271
835
  ```typescript
1272
836
  // These are equivalent:
@@ -1277,130 +841,11 @@ await client.chat.sendText({ Phone: "123", Body: "Hi" });
1277
841
  await client.message.sendText({ Phone: "123", Body: "Hi" }); // Alias
1278
842
  ```
1279
843
 
1280
- ## Advanced Usage
1281
-
1282
- ### Custom Axios Configuration
1283
-
1284
- You can extend the base client for custom axios configuration:
1285
-
1286
- ```typescript
1287
- import { BaseClient } from "wuzapi";
1288
-
1289
- class CustomClient extends BaseClient {
1290
- constructor(config: WuzapiConfig) {
1291
- super(config);
1292
-
1293
- // Add custom interceptors
1294
- this.axios.interceptors.request.use((config) => {
1295
- console.log("Making request:", config.url);
1296
- return config;
1297
- });
1298
- }
1299
- }
1300
- ```
1301
-
1302
- ### Ping Test
1303
-
1304
- Test connectivity to the API:
1305
-
1306
- ```typescript
1307
- const isConnected = await client.ping();
1308
- if (isConnected) {
1309
- console.log("API is reachable");
1310
- } else {
1311
- console.log("API is not reachable");
1312
- }
1313
- ```
1314
-
1315
- ## Examples
1316
-
1317
- For complete working examples, check the `examples/` directory:
1318
-
1319
- - **`basic-usage.js`** - Basic client setup and usage
1320
- - **`chatbot-example.js`** - Simple chatbot with command handling
1321
- - **`webhook-events-example.js`** - Comprehensive webhook event handling
1322
-
1323
- ### Complete Chat Bot Example
1324
-
1325
- ```typescript
1326
- import WuzapiClient from "wuzapi";
1327
-
1328
- const client = new WuzapiClient({
1329
- apiUrl: "http://localhost:8080",
1330
- token: "your-token",
1331
- });
1332
-
1333
- async function startBot() {
1334
- // Connect to WhatsApp
1335
- await client.session.connect({
1336
- Subscribe: ["Message"],
1337
- Immediate: false,
1338
- });
1339
-
1340
- // Wait for connection
1341
- let connected = false;
1342
- while (!connected) {
1343
- const status = await client.session.getStatus();
1344
- if (!status.LoggedIn) {
1345
- const qr = await client.session.getQRCode();
1346
- console.log("Scan this QR code:", qr.QRCode);
1347
- await new Promise((resolve) => setTimeout(resolve, 5000));
1348
- } else {
1349
- connected = true;
1350
- console.log("Bot connected and ready!");
1351
- }
1352
- }
1353
-
1354
- // Set webhook for receiving messages
1355
- await client.webhook.setWebhook("https://your-server.com/webhook");
1356
- }
1357
-
1358
- // Handle incoming messages in your webhook endpoint
1359
- function handleIncomingMessage(message: any) {
1360
- const phone = message.Info.RemoteJid.replace("@s.whatsapp.net", "");
1361
- const text = message.Message?.conversation || "";
1362
-
1363
- if (text.toLowerCase() === "hello") {
1364
- client.chat.sendText({
1365
- Phone: phone,
1366
- Body: "Hello! How can I help you today?",
1367
- });
1368
- }
1369
- }
1370
-
1371
- startBot().catch(console.error);
1372
- ```
1373
-
1374
- ### Group Management Example
1375
-
1376
- ```typescript
1377
- async function manageGroup() {
1378
- // Create a new group
1379
- const group = await client.group.create("Project Team", [
1380
- "5491155553934",
1381
- "5491155553935",
1382
- ]);
844
+ </details>
1383
845
 
1384
- console.log("Created group:", group.JID);
1385
-
1386
- // Set group photo
1387
- await client.group.setPhoto(group.JID, "data:image/jpeg;base64,...");
1388
-
1389
- // Configure group settings
1390
- await client.group.setLocked(group.JID, true); // Only admins can modify
1391
- await client.group.setEphemeral(group.JID, "7d"); // Messages disappear after 7 days
1392
-
1393
- // Get and share invite link
1394
- const invite = await client.group.getInviteLink(group.JID);
1395
- console.log("Invite link:", invite.InviteLink);
1396
- }
1397
- ```
1398
-
1399
- ## API Reference
1400
-
1401
- For detailed API documentation, refer to the [WuzAPI documentation](https://github.com/asternic/wuzapi/blob/main/API.md).
846
+ ---
1402
847
 
1403
- ## Contributing
848
+ ## 🤝 Contributing
1404
849
 
1405
850
  1. Fork the repository
1406
851
  2. Create your feature branch (`git checkout -b feature/amazing-feature`)
@@ -1423,43 +868,32 @@ npm run lint
1423
868
 
1424
869
  # Build the project
1425
870
  npm run build
1426
-
1427
- # Run development server
1428
- npm run dev
1429
871
  ```
1430
872
 
1431
- ### Code Style
1432
-
1433
- This project uses ESLint and TypeScript. Please ensure your code passes all checks:
1434
-
1435
- ```bash
1436
- npm run lint
1437
- npm run lint:fix # Auto-fix issues
1438
- ```
1439
-
1440
- ## License
873
+ ## 📄 License
1441
874
 
1442
875
  MIT License - see the [LICENSE](LICENSE) file for details.
1443
876
 
1444
- ## Support
877
+ ## 🔗 Links
1445
878
 
1446
- - 📚 [Documentation](https://github.com/asternic/wuzapi)
879
+ - 📚 [WuzAPI Documentation](https://github.com/asternic/wuzapi)
1447
880
  - 🐛 [Issue Tracker](https://github.com/gusnips/wuzapi-node/issues)
1448
881
  - 💬 [Discussions](https://github.com/gusnips/wuzapi-node/discussions)
1449
882
 
1450
- ## Changelog
883
+ ---
884
+
885
+ ## 📊 Changelog
1451
886
 
1452
- ### 1.0.0
887
+ ### Latest Updates
1453
888
 
1454
- - Initial release
1455
- - Full TypeScript support
1456
- - Complete API coverage
1457
- - Modular architecture
1458
- - Comprehensive error handling
1459
- - S3 storage integration support
1460
- - Admin user management
1461
- - Group management features
1462
- - Webhook configuration
889
+ - **Phone Pairing**: Alternative to QR code login
890
+ - **Interactive Messages**: Buttons, lists, and polls
891
+ - **Message Management**: Edit and delete messages
892
+ - **Advanced Groups**: Full participant management
893
+ - **Newsletter Support**: Business newsletter features
894
+ - **Enhanced Webhooks**: Update and delete webhook configs
895
+ - **Proxy Support**: Configure proxy for connections
896
+ - **History Sync**: Request message history after login
1463
897
 
1464
898
  ---
1465
899