wuzapi 1.2.0 → 1.4.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 +406 -70
- package/dist/client.d.ts +10 -5
- package/dist/index.js +254 -99
- package/dist/index.js.map +1 -1
- package/dist/modules/admin.d.ts +4 -3
- package/dist/modules/chat.d.ts +17 -16
- package/dist/modules/group.d.ts +10 -9
- package/dist/modules/session.d.ts +10 -10
- package/dist/modules/user.d.ts +5 -4
- package/dist/modules/webhook.d.ts +3 -2
- package/dist/types/common.d.ts +4 -1
- package/dist/types/events.d.ts +72 -0
- package/dist/types/webhook.d.ts +36 -0
- package/dist/wuzapi-client.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,8 @@ yarn add wuzapi
|
|
|
26
26
|
|
|
27
27
|
## Quick Start
|
|
28
28
|
|
|
29
|
+
### Traditional Usage (Global Token)
|
|
30
|
+
|
|
29
31
|
```typescript
|
|
30
32
|
import WuzapiClient from "wuzapi";
|
|
31
33
|
|
|
@@ -52,6 +54,48 @@ console.log("Connected:", status.Connected);
|
|
|
52
54
|
console.log("Logged In:", status.LoggedIn);
|
|
53
55
|
```
|
|
54
56
|
|
|
57
|
+
### Flexible Token Usage
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import WuzapiClient from "wuzapi";
|
|
61
|
+
|
|
62
|
+
// Create client without token (or with admin token)
|
|
63
|
+
const client = new WuzapiClient({
|
|
64
|
+
apiUrl: "http://localhost:8080",
|
|
65
|
+
token: "admin-token", // Optional: admin token as default
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Use specific user token for operations
|
|
69
|
+
const userToken = "user-specific-token";
|
|
70
|
+
|
|
71
|
+
// Connect with user token
|
|
72
|
+
await client.session.connect(
|
|
73
|
+
{
|
|
74
|
+
Subscribe: ["Message", "ReadReceipt"],
|
|
75
|
+
Immediate: false,
|
|
76
|
+
},
|
|
77
|
+
{ token: userToken }
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Send message with user token
|
|
81
|
+
await client.chat.sendText(
|
|
82
|
+
{
|
|
83
|
+
Phone: "5491155554444",
|
|
84
|
+
Body: "Hello from user-specific token!",
|
|
85
|
+
},
|
|
86
|
+
{ token: userToken }
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Admin operations with admin token (uses default)
|
|
90
|
+
const users = await client.admin.listUsers();
|
|
91
|
+
|
|
92
|
+
// Or explicitly use admin token
|
|
93
|
+
const newUser = await client.admin.addUser(
|
|
94
|
+
{ name: "John", token: "new-user-token" },
|
|
95
|
+
{ token: "admin-token" }
|
|
96
|
+
);
|
|
97
|
+
```
|
|
98
|
+
|
|
55
99
|
## Configuration
|
|
56
100
|
|
|
57
101
|
The client requires a configuration object with the following properties:
|
|
@@ -59,8 +103,45 @@ The client requires a configuration object with the following properties:
|
|
|
59
103
|
```typescript
|
|
60
104
|
interface WuzapiConfig {
|
|
61
105
|
apiUrl: string; // The WuzAPI server URL
|
|
62
|
-
token
|
|
106
|
+
token?: string; // Your user authentication token (optional)
|
|
63
107
|
}
|
|
108
|
+
|
|
109
|
+
interface RequestOptions {
|
|
110
|
+
token?: string; // Token override for specific requests
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Authentication Options
|
|
115
|
+
|
|
116
|
+
You have two ways to handle authentication:
|
|
117
|
+
|
|
118
|
+
1. **Global Token**: Set a default token in the client configuration
|
|
119
|
+
2. **Per-Request Token**: Override the token for specific API calls
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Option 1: Global token (traditional usage)
|
|
123
|
+
const client = new WuzapiClient({
|
|
124
|
+
apiUrl: "http://localhost:8080",
|
|
125
|
+
token: "your-default-token",
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Option 2: No global token, specify per request
|
|
129
|
+
const client = new WuzapiClient({
|
|
130
|
+
apiUrl: "http://localhost:8080",
|
|
131
|
+
// token is optional
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Option 3: Global token with per-request overrides
|
|
135
|
+
const client = new WuzapiClient({
|
|
136
|
+
apiUrl: "http://localhost:8080",
|
|
137
|
+
token: "admin-token", // Default admin token
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Use different token for specific operations
|
|
141
|
+
await client.chat.sendText(
|
|
142
|
+
{ Phone: "5491155554444", Body: "Hello!" },
|
|
143
|
+
{ token: "user-specific-token" }
|
|
144
|
+
);
|
|
64
145
|
```
|
|
65
146
|
|
|
66
147
|
## API Modules
|
|
@@ -109,13 +190,22 @@ await client.session.configureS3({
|
|
|
109
190
|
Send messages and manage chat interactions.
|
|
110
191
|
|
|
111
192
|
```typescript
|
|
112
|
-
// Send text message
|
|
193
|
+
// Send text message (using global token)
|
|
113
194
|
await client.chat.sendText({
|
|
114
195
|
Phone: "5491155554444",
|
|
115
196
|
Body: "Hello World!",
|
|
116
197
|
Id: "optional-message-id",
|
|
117
198
|
});
|
|
118
199
|
|
|
200
|
+
// Send with specific token override
|
|
201
|
+
await client.chat.sendText(
|
|
202
|
+
{
|
|
203
|
+
Phone: "5491155554444",
|
|
204
|
+
Body: "Hello with custom token!",
|
|
205
|
+
},
|
|
206
|
+
{ token: "user-specific-token" }
|
|
207
|
+
);
|
|
208
|
+
|
|
119
209
|
// Reply to a message
|
|
120
210
|
await client.chat.sendText({
|
|
121
211
|
Phone: "5491155554444",
|
|
@@ -247,7 +337,7 @@ await client.group.removePhoto("120362023605733675@g.us");
|
|
|
247
337
|
Manage users (requires admin token).
|
|
248
338
|
|
|
249
339
|
```typescript
|
|
250
|
-
// Create admin client
|
|
340
|
+
// Option 1: Create dedicated admin client
|
|
251
341
|
const adminClient = new WuzapiClient({
|
|
252
342
|
apiUrl: "http://localhost:8080",
|
|
253
343
|
token: "your-admin-token",
|
|
@@ -256,31 +346,43 @@ const adminClient = new WuzapiClient({
|
|
|
256
346
|
// List all users
|
|
257
347
|
const users = await adminClient.admin.listUsers();
|
|
258
348
|
|
|
349
|
+
// Option 2: Use shared client with token override
|
|
350
|
+
const client = new WuzapiClient({
|
|
351
|
+
apiUrl: "http://localhost:8080",
|
|
352
|
+
// No default token or user token as default
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// Admin operations with explicit admin token
|
|
356
|
+
const users = await client.admin.listUsers({ token: "admin-token" });
|
|
357
|
+
|
|
259
358
|
// Add new user
|
|
260
|
-
const newUser = await
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
359
|
+
const newUser = await client.admin.addUser(
|
|
360
|
+
{
|
|
361
|
+
name: "John Doe",
|
|
362
|
+
token: "user-token-123",
|
|
363
|
+
webhook: "https://example.com/webhook",
|
|
364
|
+
events: "Message,ReadReceipt",
|
|
365
|
+
proxyConfig: {
|
|
366
|
+
enabled: true,
|
|
367
|
+
proxyURL: "socks5://user:pass@proxy:port",
|
|
368
|
+
},
|
|
369
|
+
s3Config: {
|
|
370
|
+
enabled: true,
|
|
371
|
+
endpoint: "https://s3.amazonaws.com",
|
|
372
|
+
region: "us-east-1",
|
|
373
|
+
bucket: "user-media-bucket",
|
|
374
|
+
accessKey: "AKIA...",
|
|
375
|
+
secretKey: "secret...",
|
|
376
|
+
pathStyle: false,
|
|
377
|
+
mediaDelivery: "both",
|
|
378
|
+
retentionDays: 30,
|
|
379
|
+
},
|
|
279
380
|
},
|
|
280
|
-
}
|
|
381
|
+
{ token: "admin-token" }
|
|
382
|
+
);
|
|
281
383
|
|
|
282
384
|
// Delete user
|
|
283
|
-
await
|
|
385
|
+
await client.admin.deleteUser(2, { token: "admin-token" });
|
|
284
386
|
```
|
|
285
387
|
|
|
286
388
|
### Webhook Module
|
|
@@ -301,11 +403,128 @@ console.log("Subscribed events:", webhookConfig.subscribe);
|
|
|
301
403
|
|
|
302
404
|
WuzAPI sends real-time events to your webhook endpoint. Here's how to handle different types of events:
|
|
303
405
|
|
|
406
|
+
## Webhook Payload Structure
|
|
407
|
+
|
|
408
|
+
When S3 is enabled, webhook payloads will include S3 information based on the `media_delivery` setting configured in your WuzAPI instance.
|
|
409
|
+
|
|
410
|
+
### Standard Event Payload
|
|
411
|
+
|
|
412
|
+
```json
|
|
413
|
+
{
|
|
414
|
+
"event": {
|
|
415
|
+
"Info": {
|
|
416
|
+
"ID": "3EB06F9067F80BAB89FF",
|
|
417
|
+
"Type": "text",
|
|
418
|
+
"PushName": "John Doe",
|
|
419
|
+
"Timestamp": "2024-12-25T10:30:00Z",
|
|
420
|
+
"Source": {
|
|
421
|
+
"Chat": "5491155553934@s.whatsapp.net",
|
|
422
|
+
"Sender": "5491155553934@s.whatsapp.net",
|
|
423
|
+
"IsFromMe": false,
|
|
424
|
+
"IsGroup": false
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
"Message": {
|
|
428
|
+
"conversation": "Hello, this is a test message!"
|
|
429
|
+
},
|
|
430
|
+
"IsEphemeral": false,
|
|
431
|
+
"IsViewOnce": false,
|
|
432
|
+
"IsDocumentWithCaption": false,
|
|
433
|
+
"IsEdit": false
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### S3 Only (`media_delivery: "s3"`)
|
|
439
|
+
|
|
440
|
+
```json
|
|
441
|
+
{
|
|
442
|
+
"event": {
|
|
443
|
+
"Info": {
|
|
444
|
+
"ID": "3EB06F9067F80BAB89FF",
|
|
445
|
+
"Type": "image",
|
|
446
|
+
"PushName": "John Doe",
|
|
447
|
+
"Timestamp": "2024-12-25T10:30:00Z",
|
|
448
|
+
"Source": {
|
|
449
|
+
"Chat": "5491155553934@s.whatsapp.net",
|
|
450
|
+
"Sender": "5491155553934@s.whatsapp.net",
|
|
451
|
+
"IsFromMe": false,
|
|
452
|
+
"IsGroup": false
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
"Message": {
|
|
456
|
+
"imageMessage": {
|
|
457
|
+
"caption": "Check out this photo!",
|
|
458
|
+
"mimetype": "image/jpeg",
|
|
459
|
+
"width": 1920,
|
|
460
|
+
"height": 1080,
|
|
461
|
+
"fileLength": 245632
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
"IsEphemeral": false,
|
|
465
|
+
"IsViewOnce": false
|
|
466
|
+
},
|
|
467
|
+
"s3": {
|
|
468
|
+
"url": "https://my-bucket.s3.us-east-1.amazonaws.com/users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
|
|
469
|
+
"key": "users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
|
|
470
|
+
"bucket": "my-bucket",
|
|
471
|
+
"size": 245632,
|
|
472
|
+
"mimeType": "image/jpeg",
|
|
473
|
+
"fileName": "3EB06F9067F80BAB89FF.jpg"
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Both S3 and Base64 (`media_delivery: "both"`)
|
|
479
|
+
|
|
480
|
+
```json
|
|
481
|
+
{
|
|
482
|
+
"event": {
|
|
483
|
+
"Info": { "..." },
|
|
484
|
+
"Message": {
|
|
485
|
+
"imageMessage": {
|
|
486
|
+
"caption": "Check out this photo!",
|
|
487
|
+
"mimetype": "image/jpeg",
|
|
488
|
+
"width": 1920,
|
|
489
|
+
"height": 1080
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
"base64": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=",
|
|
494
|
+
"mimeType": "image/jpeg",
|
|
495
|
+
"fileName": "3EB06F9067F80BAB89FF.jpg",
|
|
496
|
+
"s3": {
|
|
497
|
+
"url": "https://my-bucket.s3.us-east-1.amazonaws.com/users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
|
|
498
|
+
"key": "users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
|
|
499
|
+
"bucket": "my-bucket",
|
|
500
|
+
"size": 245632,
|
|
501
|
+
"mimeType": "image/jpeg",
|
|
502
|
+
"fileName": "3EB06F9067F80BAB89FF.jpg"
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Base64 Only (`media_delivery: "base64"`)
|
|
508
|
+
|
|
509
|
+
```json
|
|
510
|
+
{
|
|
511
|
+
"event": { "..." },
|
|
512
|
+
"base64": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=",
|
|
513
|
+
"mimeType": "image/jpeg",
|
|
514
|
+
"fileName": "3EB06F9067F80BAB89FF.jpg"
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
304
518
|
### Basic Webhook Setup
|
|
305
519
|
|
|
306
520
|
```typescript
|
|
307
521
|
import express from "express";
|
|
308
|
-
import WuzapiClient
|
|
522
|
+
import WuzapiClient, {
|
|
523
|
+
EventType,
|
|
524
|
+
hasS3Media,
|
|
525
|
+
hasBase64Media,
|
|
526
|
+
WebhookPayload,
|
|
527
|
+
} from "wuzapi";
|
|
309
528
|
|
|
310
529
|
const app = express();
|
|
311
530
|
app.use(express.json());
|
|
@@ -315,18 +534,43 @@ const client = new WuzapiClient({
|
|
|
315
534
|
token: "your-token",
|
|
316
535
|
});
|
|
317
536
|
|
|
318
|
-
// Webhook endpoint
|
|
537
|
+
// Webhook endpoint with S3 media support
|
|
319
538
|
app.post("/webhook", async (req, res) => {
|
|
320
539
|
try {
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
// Handle
|
|
324
|
-
if (
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
540
|
+
const webhookPayload: WebhookPayload = req.body;
|
|
541
|
+
|
|
542
|
+
// Handle S3 media information if present
|
|
543
|
+
if (hasS3Media(webhookPayload)) {
|
|
544
|
+
console.log("☁️ S3 Media:", {
|
|
545
|
+
url: webhookPayload.s3.url,
|
|
546
|
+
bucket: webhookPayload.s3.bucket,
|
|
547
|
+
size: webhookPayload.s3.size,
|
|
548
|
+
type: webhookPayload.s3.mimeType,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (hasBase64Media(webhookPayload)) {
|
|
553
|
+
console.log("📦 Base64 media included");
|
|
554
|
+
// Process base64 media: webhookPayload.base64
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Extract the actual event data
|
|
558
|
+
const event = webhookPayload.event || webhookPayload;
|
|
559
|
+
const eventType = detectEventType(event);
|
|
560
|
+
|
|
561
|
+
// Handle different event types using EventType enum
|
|
562
|
+
switch (eventType) {
|
|
563
|
+
case EventType.MESSAGE:
|
|
564
|
+
await handleMessage(event);
|
|
565
|
+
break;
|
|
566
|
+
case EventType.RECEIPT:
|
|
567
|
+
await handleReceipt(event);
|
|
568
|
+
break;
|
|
569
|
+
case EventType.PRESENCE:
|
|
570
|
+
await handlePresence(event);
|
|
571
|
+
break;
|
|
572
|
+
default:
|
|
573
|
+
console.log(`Unhandled event type: ${eventType}`);
|
|
330
574
|
}
|
|
331
575
|
|
|
332
576
|
res.status(200).json({ success: true });
|
|
@@ -336,6 +580,18 @@ app.post("/webhook", async (req, res) => {
|
|
|
336
580
|
}
|
|
337
581
|
});
|
|
338
582
|
|
|
583
|
+
// Helper function to detect event type
|
|
584
|
+
function detectEventType(event: any): EventType | string {
|
|
585
|
+
if (event.Message && event.Info) return EventType.MESSAGE;
|
|
586
|
+
if (event.MessageIDs && event.Type) return EventType.RECEIPT;
|
|
587
|
+
if (event.From && typeof event.Unavailable !== "undefined")
|
|
588
|
+
return EventType.PRESENCE;
|
|
589
|
+
if (event.State && event.MessageSource) return EventType.CHAT_PRESENCE;
|
|
590
|
+
if (event.Codes) return EventType.QR;
|
|
591
|
+
// Add more detection logic as needed
|
|
592
|
+
return "Unknown";
|
|
593
|
+
}
|
|
594
|
+
|
|
339
595
|
app.listen(3000, () => {
|
|
340
596
|
console.log("Webhook server running on port 3000");
|
|
341
597
|
});
|
|
@@ -823,23 +1079,23 @@ app.get("/health", (req, res) => {
|
|
|
823
1079
|
// Initialize
|
|
824
1080
|
async function initialize() {
|
|
825
1081
|
try {
|
|
826
|
-
// Connect to WhatsApp with all events
|
|
1082
|
+
// Connect to WhatsApp with all events using EventType enum
|
|
827
1083
|
await client.session.connect({
|
|
828
1084
|
Subscribe: [
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
1085
|
+
EventType.MESSAGE,
|
|
1086
|
+
EventType.RECEIPT,
|
|
1087
|
+
EventType.PRESENCE,
|
|
1088
|
+
EventType.CHAT_PRESENCE,
|
|
1089
|
+
EventType.GROUP_INFO,
|
|
1090
|
+
EventType.CONTACT,
|
|
1091
|
+
EventType.PUSH_NAME,
|
|
1092
|
+
EventType.PICTURE,
|
|
1093
|
+
EventType.QR,
|
|
1094
|
+
EventType.CONNECTED,
|
|
1095
|
+
EventType.DISCONNECTED,
|
|
1096
|
+
EventType.LOGGED_OUT,
|
|
1097
|
+
EventType.UNDECRYPTABLE_MESSAGE,
|
|
1098
|
+
EventType.STREAM_ERROR,
|
|
843
1099
|
],
|
|
844
1100
|
Immediate: false,
|
|
845
1101
|
});
|
|
@@ -868,6 +1124,9 @@ All webhook events are fully typed. Import the specific event types you need:
|
|
|
868
1124
|
|
|
869
1125
|
```typescript
|
|
870
1126
|
import {
|
|
1127
|
+
// Event type enum for all possible events
|
|
1128
|
+
EventType,
|
|
1129
|
+
|
|
871
1130
|
// Core message types
|
|
872
1131
|
Message,
|
|
873
1132
|
MessageEvent,
|
|
@@ -887,6 +1146,16 @@ import {
|
|
|
887
1146
|
PollCreationMessage,
|
|
888
1147
|
ReactionMessage,
|
|
889
1148
|
|
|
1149
|
+
// Webhook payload types
|
|
1150
|
+
WebhookPayload,
|
|
1151
|
+
S3MediaInfo,
|
|
1152
|
+
S3OnlyWebhookPayload,
|
|
1153
|
+
Base64OnlyWebhookPayload,
|
|
1154
|
+
BothMediaWebhookPayload,
|
|
1155
|
+
hasS3Media,
|
|
1156
|
+
hasBase64Media,
|
|
1157
|
+
hasBothMedia,
|
|
1158
|
+
|
|
890
1159
|
// Event types
|
|
891
1160
|
Receipt,
|
|
892
1161
|
Presence,
|
|
@@ -904,33 +1173,64 @@ import {
|
|
|
904
1173
|
WhatsAppEvent,
|
|
905
1174
|
} from "wuzapi";
|
|
906
1175
|
|
|
907
|
-
// Type-safe event handling with
|
|
908
|
-
async function
|
|
909
|
-
const
|
|
1176
|
+
// Type-safe event handling with EventType enum
|
|
1177
|
+
async function handleTypedWebhook(webhookPayload: WebhookPayload) {
|
|
1178
|
+
const event = webhookPayload.event;
|
|
910
1179
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
const imageMsg: ImageMessage = messageContent.content;
|
|
914
|
-
console.log(`Image dimensions: ${imageMsg.width}x${imageMsg.height}`);
|
|
915
|
-
console.log(`File size: ${imageMsg.fileLength} bytes`);
|
|
1180
|
+
// Type-safe event detection using EventType enum
|
|
1181
|
+
const eventType = detectEventType(event);
|
|
916
1182
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
}
|
|
1183
|
+
switch (eventType) {
|
|
1184
|
+
case EventType.MESSAGE:
|
|
1185
|
+
const messageEvent = event as MessageEvent;
|
|
1186
|
+
const messageContent = getMessageContent(messageEvent.Message);
|
|
922
1187
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1188
|
+
if (messageContent?.type === "image") {
|
|
1189
|
+
// TypeScript knows this is an ImageMessage
|
|
1190
|
+
const imageMsg: ImageMessage = messageContent.content;
|
|
1191
|
+
console.log(`Image dimensions: ${imageMsg.width}x${imageMsg.height}`);
|
|
927
1192
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1193
|
+
// Handle S3 media if available
|
|
1194
|
+
if (hasS3Media(webhookPayload)) {
|
|
1195
|
+
console.log(`S3 URL: ${webhookPayload.s3.url}`);
|
|
1196
|
+
// Download from S3 or process as needed
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// Handle Base64 media if available
|
|
1200
|
+
if (hasBase64Media(webhookPayload)) {
|
|
1201
|
+
console.log("Processing base64 image data");
|
|
1202
|
+
// Process base64 data: webhookPayload.base64
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
break;
|
|
1206
|
+
|
|
1207
|
+
case EventType.RECEIPT:
|
|
1208
|
+
const receiptEvent = event as Receipt;
|
|
1209
|
+
console.log(`Receipt type: ${receiptEvent.Type}`);
|
|
1210
|
+
break;
|
|
1211
|
+
|
|
1212
|
+
case EventType.PRESENCE:
|
|
1213
|
+
const presenceEvent = event as Presence;
|
|
1214
|
+
console.log(`User ${presenceEvent.From.User} presence update`);
|
|
1215
|
+
break;
|
|
1216
|
+
|
|
1217
|
+
default:
|
|
1218
|
+
console.log(`Unhandled event: ${eventType}`);
|
|
931
1219
|
}
|
|
932
1220
|
}
|
|
933
1221
|
|
|
1222
|
+
// Complete webhook server with S3 support
|
|
1223
|
+
app.post("/webhook", async (req, res) => {
|
|
1224
|
+
try {
|
|
1225
|
+
const payload: WebhookPayload = req.body;
|
|
1226
|
+
await handleTypedWebhook(payload);
|
|
1227
|
+
res.status(200).json({ success: true });
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
console.error("Webhook error:", error);
|
|
1230
|
+
res.status(500).json({ error: error.message });
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1233
|
+
|
|
934
1234
|
// Helper function to handle all message types generically
|
|
935
1235
|
function processMessageContent(content: MessageContent) {
|
|
936
1236
|
switch (content.type) {
|
|
@@ -1037,6 +1337,42 @@ try {
|
|
|
1037
1337
|
}
|
|
1038
1338
|
```
|
|
1039
1339
|
|
|
1340
|
+
### Token Authentication Errors
|
|
1341
|
+
|
|
1342
|
+
When no token is provided (either globally or per-request), the library will throw a specific error:
|
|
1343
|
+
|
|
1344
|
+
```typescript
|
|
1345
|
+
import { WuzapiError } from "wuzapi";
|
|
1346
|
+
|
|
1347
|
+
// Client without global token
|
|
1348
|
+
const client = new WuzapiClient({
|
|
1349
|
+
apiUrl: "http://localhost:8080",
|
|
1350
|
+
// No token provided
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
try {
|
|
1354
|
+
// This will fail - no token provided
|
|
1355
|
+
await client.chat.sendText({
|
|
1356
|
+
Phone: "5491155554444",
|
|
1357
|
+
Body: "This will fail",
|
|
1358
|
+
});
|
|
1359
|
+
} catch (error) {
|
|
1360
|
+
if (error instanceof WuzapiError && error.code === 401) {
|
|
1361
|
+
console.error("Authentication required:", error.message);
|
|
1362
|
+
// "No authentication token provided. Either set a token in the client config or provide one in the request options."
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// Fix by providing token
|
|
1367
|
+
await client.chat.sendText(
|
|
1368
|
+
{
|
|
1369
|
+
Phone: "5491155554444",
|
|
1370
|
+
Body: "Now it works!",
|
|
1371
|
+
},
|
|
1372
|
+
{ token: "your-token" }
|
|
1373
|
+
);
|
|
1374
|
+
```
|
|
1375
|
+
|
|
1040
1376
|
### Error Types
|
|
1041
1377
|
|
|
1042
1378
|
- **Network Errors**: Connection issues, timeouts
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AxiosInstance } from 'axios';
|
|
2
|
-
import { WuzapiConfig } from './types/common.js';
|
|
2
|
+
import { WuzapiConfig, RequestOptions } from './types/common.js';
|
|
3
3
|
export declare class WuzapiError extends Error {
|
|
4
4
|
code: number;
|
|
5
5
|
details?: unknown;
|
|
@@ -9,8 +9,13 @@ export declare class BaseClient {
|
|
|
9
9
|
protected axios: AxiosInstance;
|
|
10
10
|
protected config: WuzapiConfig;
|
|
11
11
|
constructor(config: WuzapiConfig);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the token from request options or instance config
|
|
14
|
+
* Throws an error if no token is available
|
|
15
|
+
*/
|
|
16
|
+
private resolveToken;
|
|
17
|
+
protected request<T>(method: "GET" | "POST" | "DELETE", endpoint: string, data?: unknown, options?: RequestOptions): Promise<T>;
|
|
18
|
+
protected get<T>(endpoint: string, options?: RequestOptions): Promise<T>;
|
|
19
|
+
protected post<T>(endpoint: string, data?: unknown, options?: RequestOptions): Promise<T>;
|
|
20
|
+
protected delete<T>(endpoint: string, options?: RequestOptions): Promise<T>;
|
|
16
21
|
}
|