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 +533 -1099
- package/dist/client.d.ts +11 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +327 -98
- package/dist/index.js.map +1 -1
- package/dist/modules/admin.d.ts +12 -3
- package/dist/modules/chat.d.ts +38 -17
- package/dist/modules/group.d.ts +35 -10
- package/dist/modules/newsletter.d.ts +9 -0
- package/dist/modules/session.d.ts +23 -11
- package/dist/modules/user.d.ts +10 -5
- package/dist/modules/webhook.d.ts +12 -3
- package/dist/types/chat.d.ts +55 -0
- package/dist/types/common.d.ts +4 -1
- package/dist/types/group.d.ts +52 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/newsletter.d.ts +17 -0
- package/dist/types/session.d.ts +16 -0
- package/dist/types/user.d.ts +5 -0
- package/dist/types/webhook.d.ts +9 -0
- package/dist/wuzapi-client.d.ts +4 -2
- package/package.json +1 -1
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
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
+
## 🔧 Configuration
|
|
58
68
|
|
|
59
69
|
```typescript
|
|
60
70
|
interface WuzapiConfig {
|
|
61
|
-
apiUrl: string;
|
|
62
|
-
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
|
-
##
|
|
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
|
-
|
|
256
|
+
---
|
|
69
257
|
|
|
70
|
-
|
|
258
|
+
## 📖 Complete API Reference
|
|
71
259
|
|
|
72
|
-
|
|
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
|
-
|
|
321
|
+
</details>
|
|
108
322
|
|
|
109
|
-
|
|
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/
|
|
351
|
+
Image: "data:image/jpeg;base64,/9j/4AAQ...",
|
|
133
352
|
Caption: "Check this out!",
|
|
134
353
|
});
|
|
135
354
|
|
|
136
|
-
// Send
|
|
137
|
-
await client.chat.
|
|
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
|
-
|
|
140
|
-
Footer: "
|
|
387
|
+
Body: "Choose an option:",
|
|
388
|
+
Footer: "Select one:",
|
|
141
389
|
Buttons: [
|
|
142
|
-
{
|
|
143
|
-
{
|
|
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
|
|
150
|
-
await client.chat.
|
|
395
|
+
// Send list message
|
|
396
|
+
await client.chat.sendList({
|
|
151
397
|
Phone: "5491155554444",
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
158
|
-
await client.chat.
|
|
412
|
+
// Send poll
|
|
413
|
+
await client.chat.sendPoll({
|
|
159
414
|
Phone: "5491155554444",
|
|
160
|
-
Name: "
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
//
|
|
166
|
-
await client.chat.
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
488
|
+
</details>
|
|
189
489
|
|
|
190
|
-
|
|
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
|
-
|
|
514
|
+
</details>
|
|
207
515
|
|
|
208
|
-
|
|
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
|
|
227
|
-
await client.group.
|
|
542
|
+
// Set group topic/description
|
|
543
|
+
await client.group.setTopic(
|
|
228
544
|
"120362023605733675@g.us",
|
|
229
|
-
"
|
|
545
|
+
"Welcome to our group! Please read the rules."
|
|
230
546
|
);
|
|
231
547
|
|
|
232
|
-
//
|
|
233
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
578
|
+
// Get group invite information
|
|
579
|
+
const inviteInfo = await client.group.getInviteInfo("https://chat.whatsapp.com/XXXXXXXXX");
|
|
248
580
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
631
|
+
</details>
|
|
287
632
|
|
|
288
|
-
|
|
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
|
-
|
|
645
|
+
// Update webhook URL
|
|
646
|
+
await client.webhook.updateWebhook("https://my-new-server.com/webhook");
|
|
301
647
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
648
|
+
// Delete webhook configuration
|
|
649
|
+
await client.webhook.deleteWebhook();
|
|
650
|
+
```
|
|
305
651
|
|
|
306
|
-
|
|
652
|
+
</details>
|
|
307
653
|
|
|
308
|
-
|
|
654
|
+
<details>
|
|
655
|
+
<summary><strong>📰 Newsletter Module</strong> - Newsletter management (Business accounts only)</summary>
|
|
309
656
|
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
673
|
+
## 🎣 Webhook Event Handling
|
|
406
674
|
|
|
407
|
-
|
|
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
|
|
439
|
-
|
|
440
|
-
// Handle S3 media
|
|
693
|
+
const webhookPayload = req.body;
|
|
694
|
+
|
|
695
|
+
// Handle S3 media if present
|
|
441
696
|
if (hasS3Media(webhookPayload)) {
|
|
442
|
-
console.log("
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
754
|
+
## 🛠️ Advanced Topics
|
|
1214
755
|
|
|
1215
|
-
|
|
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
|
|
780
|
+
### Common Error Codes
|
|
781
|
+
- **401**: Authentication required
|
|
782
|
+
- **404**: Endpoint not found
|
|
783
|
+
- **500**: Server error
|
|
1239
784
|
|
|
1240
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
830
|
+
</details>
|
|
1268
831
|
|
|
1269
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
883
|
+
---
|
|
884
|
+
|
|
885
|
+
## 📊 Changelog
|
|
1451
886
|
|
|
1452
|
-
###
|
|
887
|
+
### Latest Updates
|
|
1453
888
|
|
|
1454
|
-
-
|
|
1455
|
-
-
|
|
1456
|
-
-
|
|
1457
|
-
-
|
|
1458
|
-
-
|
|
1459
|
-
-
|
|
1460
|
-
-
|
|
1461
|
-
-
|
|
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
|
|